Thread: Trouble with subqueries
I have the following two tables: create table movies ( name varchar(80), info varchar(80), length int, primary key (name) ); create table ratings ( name varchar(80), userid varchar(10), rating char(1), foreign key (name) references movies, primarykey(name, userid) ); The tables contain movies and users' ratings of the movies. I would like to get a listing of all the movies along with how many users have given the movie some particular rating. The first solution that I came up with was this: SELECT name, length, fives FROM movies, (SELECT name as rname, count(*) as fives FROM ratings WHERE rating='5' GROUP BY name) WHERE name=rname; but in PostgreSQL 7 it just gives me this error message: ERROR: parser: parse error at or near "(" I have previously used similar queries in Oracle where they have worked, so it would seem to me that PostgreSQL doesn't support subselects after all despite all the claims. Am I doing something wrong or/and is there some another way of making this query that would work in PostgreSQL?
Jussi Vainionpää <jjvainio@cc.hut.fi> writes: > SELECT name, length, fives > FROM movies, > (SELECT name as rname, > count(*) as fives > FROM ratings > WHERE rating='5' > GROUP BY name) > WHERE name=rname; > > but in PostgreSQL 7 it just gives me this error message: > ERROR: parser: parse error at or near "(" > I have previously used similar queries in Oracle where they have worked, > so it would seem to me that PostgreSQL doesn't support subselects after > all despite all the claims. It allows subselects in the WHERE clause, but not in the FROM clause. > Am I doing something wrong or/and is there some another way of making > this query that would work in PostgreSQL? What you can do, is create a view with your subselect, which you can then use in the FROM clause. Tomas
Hello Jussi, Once, Friday, January 19, 2001, 12:34:50 PM, you wrote: JV> I have the following two tables: JV> create table movies ( JV> name varchar(80), JV> info varchar(80), JV> length int, JV> primary key (name) JV> ); JV> create table ratings ( JV> name varchar(80), JV> userid varchar(10), JV> rating char(1), JV> foreign key (name) references movies, JV> primary key(name, userid) JV> ); JV> The tables contain movies and users' ratings of the movies. JV> I would like to get a listing of all the movies along with how many JV> users have given the movie some particular rating. The first solution JV> that I came up with was this: JV> SELECT name, length, fives JV> FROM movies, JV> (SELECT name as rname, JV> count(*) as fives JV> FROM ratings JV> WHERE rating='5' JV> GROUP BY name) JV> WHERE name=rname; JV> but in PostgreSQL 7 it just gives me this error message: JV> ERROR: parser: parse error at or near "(" JV> I have previously used similar queries in Oracle where they have worked, JV> so it would seem to me that PostgreSQL doesn't support subselects after JV> all despite all the claims. JV> Am I doing something wrong or/and is there some another way of making JV> this query that would work in PostgreSQL? If I understand correctly it must looks like this: SELECT name, length, (SELECT count(*) FROM ratings WHERE rating='5' and rating.name=movies.name)as fives FROM movies WHERE name=rname; -- Best regards,Yury
Yury Don wrote: > If I understand correctly it must looks like this: > SELECT name, length, > (SELECT count(*) > FROM ratings > WHERE rating='5' > and rating.name=movies.name) as fives > FROM movies > WHERE name=rname; This results in several rows for each movie, which can be fixed by using select distint, but I don't quite understand why that happens. Any ideas? The method suggested by Tomas Berndtsson involving an intermediate view works nicely too. But this was not quite what I was looking for, as I would like to have all the movies in the list, also the ones with no ratings. The fives column should just be zero for those. I though about creating a view of the union of the movies table and these results and then doing select max(fives) group by name; from that view, but it seems that views with unions are not allowed. But I did find a solution: SELECT movies.name, movies.length, COUNT(CASE WHEN ratings.name=movies.name AND rating='5' THEN true END)AS fives FROM ratings, movies GROUP BY movies.name, movies.length; But I don't quite understand why I need to have movies.length in the GROUP BY -clause?
Stuff like this is possible in 7.1: SELECT m.name, m.length, count(r.rating) AS fives FROM movies m LEFT JOIN (SELECT * FROM ratings WHERE rating = 5) AS r ON m.name = r.name GROUP BY m.name, m.length; I think that would work. You'd want to try different queries with EXPLAIN to see what looks best. > But I did find a solution: > SELECT movies.name, movies.length, > COUNT(CASE WHEN ratings.name=movies.name > AND rating='5' THEN true END) AS fives > FROM ratings, movies GROUP BY movies.name, movies.length; > > But I don't quite understand why I need to have movies.length in the GROUP > BY -clause? When doing a GROUP BY, you can only select grouped columns. You cannot select other columns (except in group aggregates) since there can be more than one possible value for them if the group has more than one row. The database can't know which row in the group from which to get the length field. If length is grouped, there is only one possible value for it in the whole group, so I knows what value to get (the only one). Group aggregates are allowed on the ungrouped columns (and the grouped columns too) since it is not ambiguous - not single value to trying to be selected. When you do a GROUP BY, your table is partitioned into blocks of rows where the GROUPed BY columns are the same for all rows in the group. Only one row can result from each group of a grouped table. Aggregate functions used in returning a group row from a grouped table are aggregates on the group returned by that row, not the whole (ungrouped) table. Hope that makes sense. -- -------- Robert B. Easter reaster@comptechnews.com --------- -- CompTechNews Message Board http://www.comptechnews.com/ -- -- CompTechServ Tech Services http://www.comptechserv.com/ -- ---------- http://www.comptechnews.com/~reaster/ ------------