自从PostgreSQL能够进行LATERAL
连接以来,我一直在阅读它,因为我目前正在为我的团队做复杂的数据转储,其中有许多低效的子查询,使得整个查询需要四分钟或更长时间。
我知道LATERAL
连接可能对我有帮助,但即使阅读了Heap Analytics的this one等文章,我仍然不太理解。LATERAL
连接的用例是什么?LATERAL
连接和子查询有什么区别?
自从PostgreSQL能够进行LATERAL
连接以来,我一直在阅读它,因为我目前正在为我的团队做复杂的数据转储,其中有许多低效的子查询,使得整个查询需要四分钟或更长时间。
我知道LATERAL
连接可能对我有帮助,但即使阅读了Heap Analytics的this one等文章,我仍然不太理解。LATERAL
连接的用例是什么?LATERAL
连接和子查询有什么区别?
5条答案
按热度按时间7jmck4yq1#
什么是
LATERAL
连接?该功能是在PostgreSQL 9.3中引入的。手册:
出现在
FROM
中的子查询可以在关键字LATERAL
之前。这允许它们引用前面FROM
项提供的列。(没有LATERAL
,每个子查询都是独立计算的,因此不能交叉引用任何其他FROM
项。)在
FROM
中出现的表函数也可以在关键字LATERAL
之前,但对于函数,关键字是可选的;函数的参数可以包含对前面FROM
项提供的列的引用。这里给出了基本的代码示例。
更像是一个 correlated 子查询
LATERAL
连接更像是correlated subquery,而不是普通子查询,因为LATERAL
连接右侧的表达式会为它左侧的每一行计算一次--就像 correlated 子查询一样--而普通子查询(表表达式)只计算 * 一次 *。(不过查询规划器有办法优化这两种情况的性能。)相关的答案与代码的例子,为双方并排,解决同样的问题:
对于返回 * 多个列 ,
LATERAL
连接通常更简单,更干净,更快。另外,请记住,相关子查询的等效项是*
LEFT JOIN LATERAL ... ON true
**:子查询不能做的事情
LATERAL
连接可以做很多事情,但是(相关)子查询不能(容易地)。相关子查询只能返回单个值,不是多列,也不是多行--空函数调用除外(如果返回多行,则将结果行相乘).但即使是某些集合返回函数也只允许在FROM
子句中使用。例如在Postgres 9.4或更高版本中使用多个参数的unnest()
。手册:这仅在
FROM
子句中允许;所以这是可行的,但不能(容易地)被替换为子查询:
字符串
FROM
子句中的逗号(,
)是CROSS JOIN
的缩写。LATERAL
是表函数自动假定的。关于
UNNEST( array_expression [, ... ] )
的特殊情况:SELECT
列表中设置返回函数你也可以直接在
SELECT
列表中使用像unnest()
这样的返回集合的函数。在Postgres 9.6之前,在同一个SELECT
列表中使用多个这样的函数会出现令人惊讶的行为。但是它最终在Postgres 10中被清理掉了,现在是一个有效的替代方案(即使不是标准SQL)。请参阅:基于上述示例:
型
比较:
fiddle for pg 9.6的
fiddle for pg 10的
注意:
SELECT
列表中的一个(组合)集返回函数产生no rows删除了该行。在内部它转换为CROSS JOIN LATERAL ROWS FROM ...
,而不是LEFT JOIN LATERAL ... ON true
!fiddle for pg 16显示差异。
澄清误传
手册:
对于
INNER
和OUTER
连接类型,必须指定连接条件,即NATURAL
、ON
join_condition 或USING
(*join_column * [,...])中的一个。有关含义,请参见下文。对于
CROSS JOIN
,这些子句都不能出现。所以这两个查询是有效的(即使不是特别有用):
型
而这一个不是:
型
这就是为什么Andomar's代码示例是正确的(
CROSS JOIN
不需要连接条件),而Attila's不是。v7pvogib2#
非
lateral
连接和lateral
连接之间的区别在于是否可以查看左手表的行。例如:字符串
这种“向外看”意味着子查询必须被计算不止一次,毕竟
t1.col1
可以假定许多值。相比之下,非
lateral
连接后的子查询只能计算一次:型
如果没有
lateral
,内部查询不依赖于外部查询。lateral
查询是correlated
查询的一个例子,因为它与查询本身之外的行有关系。q0qdq0h23#
数据库表
使用以下
blog
数据库表存储我们平台托管的博客:x1c 0d1x的数据
目前我们有两个博客:
| ID|创建于|标题|URL|
| --|--|--|--|
| 1 |2013-09-30 2013-09-30| Vlad Mihalcea的博客|https://vladmihalcea.com|
| 2 |2017-01-22 2017-01-22|超持久性|https://hypersistence.io的|
不使用SQL LATERAL JOIN获取报表
我们需要构建一个报告,从
blog
表中提取以下数据:如果你使用的是PostgreSQL,那么你必须执行以下SQL查询:
字符串
正如您所看到的,
age_in_years
必须定义三次,因为在计算next_anniversary
和days_to_next_anniversary
值时需要它。而这正是LATERAL JOIN可以帮助我们的地方。
使用SQL LATERAL JOIN获取报表
以下关系数据库系统支持
LATERAL JOIN
语法:SQL Server可以使用
CROSS APPLY
和OUTER APPLY
模拟LATERAL JOIN
。LATERAL JOIN允许我们重用
age_in_years
值,并在计算next_anniversary
和days_to_next_anniversary
值时进一步传递它。前面的查询可以重写为使用LATERAL JOIN,如下所示:
型
并且,
age_in_years
值可以计算为1,并重新用于next_anniversary
和days_to_next_anniversary
计算:| blog_id|年龄|下一周年|距下一周年纪念日的天数|
| --|--|--|--|
| 1 | 7 |2021-09-30 -09- 09 - 09| 295 |
| 2 | 3 |2021-01-22 2021-01-22 2021-01-22| 44 |
好多了,对吧?
age_in_years
是为blog
表的每一条记录计算的。因此,它的工作方式类似于相关子查询,但子查询记录与主表连接,因此,我们可以引用子查询产生的列。gywdnpxw4#
有一件事没有人指出,那就是您可以使用
LATERAL
查询在每个选定的行上应用用户定义的函数。例如:
字符串
这是我所知道的在PostgreSQL中做这类事情的唯一方法。
swvgeqrz5#
首先,Lateral and Cross Apply is same thing。因此,您还可以阅读有关交叉应用的信息。由于它在SQL Server中实现了很长时间,您将找到有关它的更多信息,然后横向。
第二,根据我的理解,没有什么你不能用subquery代替lateral。但是:
考虑以下查询。
字符串
在这种情况下,你可以使用侧写。
型
在此查询中,由于限制子句的原因,您不能使用普通连接。可以使用横向或交叉应用when there is not simple join condition。
有更多的用法为横向或交叉适用,但这是最常见的一个我发现。