LATERAL和PostgreSQL中的子查询之间有什么区别?

由于Postgres具有执行LATERAL连接的function,因此我一直在阅读,因为我目前正在为我的团队进行复杂的数据转储,导致大量低效的子查询,使整个查询花费四分钟或更长时间。

我明白, LATERALjoin可能会帮助我,但即使从Heap Analytics阅读这样的文章之后,我仍然不太理解。

什么是LATERAL连接的用例? LATERAL连接和子查询有什么区别?

更像是一个相关的子查询

一个LATERAL连接(Postgres 9.3+)更像是一个相关的子查询 ,而不是一个普通的子查询。 就像@Andomar指出的那样 ,一个LATERAL连接权限的函数或子查询通常必须被多次计算 – 对于LATERAL连接剩下的每一行,就像一个相关的子查询一样 – 而一个纯粹的子查询(expression式)是只评估一次 。 (查询计划人员虽然有办法优化性能。
这个相关的答案有两个并排的代码示例,解决了同样的问题:

  • 优化GROUP BY查询以检索每个用户的最新logging

对于返回多个列LATERAL连接通常更简单,更干净,更快速。 另外,请记住相关的子查询的等价物是LEFT JOIN LATERAL ... ON true

  • 使用数组参数多次调用set-returning函数

阅读LATERAL手册

这比我们将要在这里得到答案的任何东西都更具权威性:

子查询不能做的事情

一些 LATERAL连接可以做的事情,但(相关的)子查询不能(容易)。 一个相关的子查询只能返回一个单一的值,而不是多个列而不是多个行 – 除了裸函数调用(如果它们返回多行,则返回结果行)。 但是,即使某些设置返回函数只能在FROM子句中使用。 就像Postgres 9.4中带有多个参数的新unnest()一样。 手册:

这只在FROM子句中是允许的。

所以这个工作,但不能轻易地被一个子查询replace:

 CREATE TABLE tbl (a1 int[], a2 int[]); SELECT * FROM tbl t, unnest(t.a1, t.a2) u(elem1, elem2); -- implicit LATERAL 

FROM子句中的逗号( , )是CROSS JOIN简称。
LATERAL被自动假定为表函数。)

关于UNNEST( array_expression [, ... ] )这个问题的更多关于UNNEST( array_expression [, ... ] )特例:

  • 你如何声明一个只能在FROM子句中使用的set-returning-function?

SELECT列表中设置返回函数

你也可以直接在SELECT列表中使用像unnest()这样的设置返回函数。 这用于展示令人惊讶的行为,在同一个SELECT列表中有多个实例,直到Postgres 9.6。 但它终于被Postgres 10消毒了,现在是一个有效的select(即使不是标准的SQL)。
基于上面的例子:

 SELECT *, unnest(t.a1) AS elem1, unnest(t.a2) AS elem2 FROM tbl t; 

比较:

这里是pg 9.6的dbfiddle
这里是第10页的dbfiddle

澄清错误信息

该手册在此澄清误导性信息:

对于INNEROUTER连接types,必须指定连接条件,即NATURALON join_conditionUSINGjoin_column [,…])中的一个。 见下面的意思。
对于CROSS JOIN ,这些子句都不能出现。

所以这两个查询是有效的(即使不是特别有用):

 SELECT * FROM tbl t LEFT JOIN LATERAL (SELECT * FROM b WHERE b.t_id = t.t_id) t ON TRUE ; SELECT * FROM tbl t, LATERAL (SELECT * FROM b WHERE b.t_id = t.t_id) t; 

虽然这不是:

 SELECT * FROM tbl t LEFT JOIN LATERAL (SELECT * FROM b WHERE b.t_id = t.t_id) t; 

这就是为什么@ Andomar的代码示例是正确的( CROSS JOIN不需要连接条件), @ Attila的 无效的。

laterallateral连接的区别在于您是否可以查看左侧表格的行。 例如:

 select * from table1 t1 cross join lateral ( select * from t2 where t1.col1 = t2.col1 -- Only allowed because of lateral ) sub 

这个“向外看”意味着子查询必须被多次评估。 毕竟, t1.col1可以承担很多的价值。

相反,非lateral连接后的子查询可以被评估一次:

 select * from table1 t1 cross join ( select * from t2 where t2.col1 = 42 -- No reference to outer query ) sub 

根据需要没有lateral ,内部查询不依赖于外部查询。 lateral查询是correlated查询的一个例子,因为它与查询本身之外的行之间的关系。

首先, 横向和交叉应用是一回事 。 因此,您也可以阅读有关交叉申请。 由于它已经在SQL Server中实现了很长时间,所以你会发现更多关于它的信息,然后是Lateral。

其次, 根据我的理解 ,没有什么你不能使用子查询而不是使用横向。 但:

考虑以下查询。

 Select A.* , (Select B.Column1 from B where B.Fk1 = A.PK and Limit 1) , (Select B.Column2 from B where B.Fk1 = A.PK and Limit 1) FROM A 

你可以在这种情况下使用横向。

 Select A.* , x.Column1 , x.Column2 FROM A LEFT JOIN LATERAL ( Select B.Column1,B.Column2,B.Fk1 from B Limit 1 ) x ON X.Fk1 = A.PK 

在这个查询中,由于限制子句,你不能使用正常的连接。 当没有简单的连接条件时,可以使用横向或交叉应用。

有更多的横向或交叉适用的用法,但这是我发现最常见的。