Since Postgres came out with the ability to do LATERAL
joins, I've been reading up on it, since I currently do complex data dumps for my team with lots of inefficient subqueries that make the overall query take four minutes or more.
I understand that LATERAL
joins may be able to help me, but even after reading articles like this one from Heap Analytics, I still don't quite follow.
What is the use case for a LATERAL
join? What is the difference between a LATERAL
join and a subquery?
More like a correlated subquery
A
LATERAL
join (Postgres 9.3+) is more like a correlated subquery, not a plain subquery. Like @Andomar pointed out, a function or subquery to the right of aLATERAL
join typically has to be evaluated many times - once for each row left of theLATERAL
join - just like a correlated subquery - while a plain subquery (table expression) is evaluated once only. (The query planner has ways to optimize performance for either, though.)This related answer has code examples for both side by side, solving the same problem:
For returning more than one column, a
LATERAL
join is typically simpler, cleaner and faster. Also, remember that the equivalent of a correlated subquery isLEFT JOIN LATERAL ... ON true
:Read the manual for on
LATERAL
It is more authoritative than anything we are going to put into answers here:
Things a subquery can't do
There are things that a
LATERAL
join can do, but a (correlated) subquery cannot (easily). A correlated subquery can only return a single value, not multiple columns and not multiple rows - with the exception of bare function calls (which multiply result rows if they return multiple rows). But even certain set-returning functions are only allowed in theFROM
clause. Like the newunnest()
with multiple parameters in Postgres 9.4. The manual:So this works, but cannot easily be replaced with a subquery:
(The comma (
,
) in theFROM
clause is short notation forCROSS JOIN
.LATERAL
is assumed automatically for table functions.)More about the special case of
UNNEST( array_expression [, ... ] )
under this later question on dba.SE:Set-returning functions in the
SELECT
listYou can also use set-returning functions like
unnest()
in theSELECT
list directly. This used to exhibit surprising behavior with more than one instance in the sameSELECT
list up to Postgres 9.6. But it has finally been sanitized with Postgres 10 and is a valid alternative now (even if not standard SQL).Building on above example:
Comparison:
dbfiddle for pg 9.6 here
dbfiddle for pg 10 here
Clarify misinformation
The manual clarifies misleading information here:
So these two queries are valid (even if not particularly useful):
While this one isn't:
That's why @Andomar's code example is correct (the
CROSS JOIN
does not require a join condition) and @Attila'siswas invalid.The difference between a non-
lateral
and alateral
join lies in whether you can look to the left hand table's row. For example:This "outward looking" means that the subquery has to be evaluated more than once. After all,
t1.col1
can assume many values.By contrast, the subquery after a non-
lateral
join can be evaluated once:As is required without
lateral
, the inner query does not depend in any way on the outer query. Alateral
query is an example of acorrelated
query, because of its relation with rows outside the query itself.One thing no one has pointed out is that you can use
LATERAL
queries to apply a user-defined function on every selected row.For instance:
That's the only way I know how to do this sort of thing in PostgreSQL.
First, Lateral and Cross Apply is same thing. Therefore you may also read about Cross Apply. Since it was implemented in SQL Server for ages, you will find more information about it then Lateral.
Second, according to my understanding, there is nothing you can not do using subquery instead of using lateral. But:
Consider following query.
You can use lateral in this condition.
In this query you can not use normal join, due to limit clause. Lateral or Cross Apply can be used when there is not simple join condition.
There are more usages for lateral or cross apply but this is most common one I found.