Answers:
甲LATERAL
加入(Postgres的9.3或更高版本)更像是一个相关子查询,而不是一个简单的子查询。就像Andomar指出的那样,联接右边的函数或子查询必须LATERAL
为它的每一行评估一次(就像相关的子查询一样),而普通子查询(表表达式)只评估一次。(不过,查询计划者可以通过其中一种方法来优化性能。)
这个相关的答案同时提供了示例代码,可以解决相同的问题:
对于返回多个列,LATERAL
联接通常更简单,更干净,更快。
另外,请记住,相关子查询的等效项是LEFT JOIN LATERAL ... ON true
:
LATERAL
它比我们在此处要回答的任何问题更具权威性:
有是事,一个LATERAL
连接可以做,但一(相关的)子查询不能(容易)。相关的子查询只能返回一个值,不能返回多列,不能返回多行-裸函数调用除外(如果返回多行,结果行将相乘)。但是,即使某些子集返回函数也只能在该FROM
子句中使用。与unnest()
Postgres 9.4或更高版本中的多个参数一样。手册:
这仅在
FROM
子句中允许;
因此这可行,但不能轻易用子查询替换:
CREATE TABLE tbl (a1 int[], a2 int[]);
SELECT * FROM tbl, unnest(a1, a2) u(elem1, elem2); -- implicit LATERAL
子句中的逗号(,
)FROM
是的缩写CROSS JOIN
。
LATERAL
表功能自动假定。
有关以下特殊情况的更多信息UNNEST( array_expression [, ... ] )
:
SELECT
列表中的返回集合函数您也可以直接unnest()
在SELECT
列表中使用返回集合的函数。在过去的SELECT
Postgres 9.6列表中,它经常表现出令人惊讶的行为,并具有多个这样的功能。但是它终于用Postgres 10进行了消毒,现在是有效的替代方法(即使不是标准的SQL)。看到:
在以上示例的基础上:
SELECT *, unnest(a1) AS elem1, unnest(a2) AS elem2
FROM tbl;
比较:
dbfiddle为PG 9.6 这里
dbfiddle为第10页这里
对于
INNER
和OUTER
连接类型,必须指定一个连接条件,即NATURAL
、、ON
join_condition或USING
(join_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
不需要连接条件),并@阿提拉 就是无效。
LATERAL
子查询中的窗口函数:gis.stackexchange.com/a/230070/7244
非联接lateral
和lateral
联接之间的区别在于您是否可以查看左侧表格的行。例如:
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
,因为它与查询本身以外的行关系查询。
select * from table1 left join t2 using (col1)
比较?对我尚不清楚何时使用/ on条件的联接不足,并且使用侧向更为有意义。
首先,横向和交叉应用是同一回事。因此,您可能还会阅读有关交叉申请的信息。由于它是在SQL Server中实现了很长时间,因此,您会发现它的详细信息,然后是“横向”。
其次,根据我的理解,没有什么可以使用子查询而不是横向查询来完成的。但:
考虑以下查询。
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
在此查询中,由于limit子句,您不能使用普通联接。当没有简单的连接条件时,可以使用横向或交叉应用。
横向或交叉应用的用法更多,但这是我发现的最常见的用法。
lateral
而不是apply
。也许微软为该语法申请了专利?
lateral
在SQL标准中,但apply
不是。
LEFT JOIN
需要连接条件。ON TRUE
除非您想以某种方式进行限制,否则就这么做。
cross join
或on
条件,否则您将得到一个错误
没有人指出的一件事是,您可以使用LATERAL
查询在每个选定的行上应用用户定义的函数。
例如:
CREATE OR REPLACE FUNCTION delete_company(companyId varchar(255))
RETURNS void AS $$
BEGIN
DELETE FROM company_settings WHERE "company_id"=company_id;
DELETE FROM users WHERE "company_id"=companyId;
DELETE FROM companies WHERE id=companyId;
END;
$$ LANGUAGE plpgsql;
SELECT * FROM (
SELECT id, name, created_at FROM companies WHERE created_at < '2018-01-01'
) c, LATERAL delete_company(c.id);
那是我知道如何在PostgreSQL中做这种事情的唯一方法。
apply
是一样的lateral
,从SQL标准)