Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Oracle subquery does not see the variable from the outer block 2 levels up

I'd like to get in one query a post and the first comment associated with the post. Here is how I do it in PostgreSQL:

SELECT p.post_id,  (select * from   (select comment_body from comments where post_id = p.post_id   order by created_date asc) where rownum=1 ) the_first_comment FROM posts p   

and it works fine.

However, in Oracle I'm getting an error ORA-00904 p.post_id: invalid identifier.

It seems to work fine for one subselect, but I cannot get the comment with only one due to the fact that I need to use rownum (no limit / offset in Oracle).

What am I doing wrong here?

like image 687
user248789 Avatar asked Jan 12 '10 10:01

user248789


People also ask

Which subquery use value from outer query?

In a SQL database query, a correlated subquery (also known as a synchronized subquery) is a subquery (a query nested inside another query) that uses values from the outer query.

How many levels of subquery can be written?

A subquery can be nested inside the WHERE or HAVING clause of an outer SELECT , INSERT , UPDATE , or DELETE statement, or inside another subquery. Up to 32 levels of nesting is possible, although the limit varies based on available memory and the complexity of other expressions in the query.

What type of subquery contains a reference to a column in the outer query?

Type of SubqueriesCorrelated subqueries : Reference one or more columns in the outer SQL statement. The subquery is known as a correlated subquery because the subquery is related to the outer SQL statement.

In which kind of subquery the outer query is executed first?

In Correlated Query, Outer query executes first and for every Outer query row Inner query is executed. Hence, Inner query uses values from Outer query.


2 Answers

No, Oracle doesn't correlate the subqueries nested more than one level deep (and neither does MySQL).

This is a well-known problem.

Use this:

SELECT  p.post_id, c.* FROM    posts JOIN    (         SELECT  c.*, ROW_NUMBER() OVER (PARTITION BY post_id ORDER BY created_date ASC) AS rn         FROM    comments c         ) c ON      c.post_id = p.post_id         AND rn = 1 
like image 94
Quassnoi Avatar answered Sep 19 '22 03:09

Quassnoi


If you need SQL that is platform-independent, this will work:

SELECT p.post_id      , c.comment_body   FROM posts p      , comments c  WHERE p.post_id = c.post_id    AND c.created_date IN        ( SELECT MIN(c2.created_date)            FROM comments c2           WHERE c2.post_id = p.post_id         ); 

But it assumes that (post_id, created_date) is the primary key of comments. If it isn't, you're going to get more than one line posts that have comments with the same created_date.

Also, it is likely to be slower than the solution that uses analytics, given by Quassnoi.

like image 43
Steve Broberg Avatar answered Sep 22 '22 03:09

Steve Broberg