Thread: Trouble with subqueries

Trouble with subqueries

From
Jussi Vainionpää
Date:
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?





Re: Trouble with subqueries

From
Tomas Berndtsson
Date:
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


Re: Trouble with subqueries

From
Yury Don
Date:
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




Re: Trouble with subqueries

From
Jussi Vainionpää
Date:
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?




Re: Trouble with subqueries

From
"Robert B. Easter"
Date:
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/ ------------