lateral join是让子查询能引用左侧表字段的唯一合法方式,本质是逐行执行子查询并动态绑定外层列;普通join右侧必须为独立结果集,无法在子查询中直接引用左表字段。

什么是 LATERAL JOIN,它和普通 JOIN 有什么本质区别
LATERAL JOIN 不是“多加个关键字就能用”的语法糖,它是让子查询能引用左侧表字段的唯一合法方式。普通 JOIN 的右侧必须是独立结果集,而 LATERAL 允许你在子查询里写 WHERE user_id = users.id 这类依赖主表字段的条件——没有 LATERAL,这会直接报错 ERROR: invalid reference to FROM-clause entry。
常见误操作是把子查询写成 (SELECT ... FROM orders WHERE user_id = users.id) 却忘了加 LATERAL,PostgreSQL 和 BigQuery 会立刻拒绝;MySQL 目前不支持 LATERAL,别白费劲。
- 只在 PostgreSQL 9.3+、BigQuery、Trino(PrestoSQL)中可用,SQLite 和 SQL Server 不支持
- 子查询必须用括号包裹,且紧跟
LATERAL关键字:LATERAL (SELECT ...),不能写成JOIN LATERAL SELECT ... - 子查询返回 0 行时,主表该行被丢弃(类似 INNER JOIN),想保留得用
LEFT JOIN LATERAL
用 LATERAL JOIN 实现“每行取最新订单”这类典型行级计算
比如要查每个用户的最新一笔订单金额,不能靠 GROUP BY user_id + MAX(order_time) 硬凑,因为金额和时间不是同一行数据。这时候 LATERAL 是最干净的解法:
SELECT u.name, latest_order.amount FROM users u LEFT JOIN LATERAL ( SELECT amount FROM orders o WHERE o.user_id = u.id ORDER BY o.order_time DESC LIMIT 1 ) AS latest_order ON true;
关键点在于:子查询里的 o.user_id = u.id 能生效,且 LIMIT 1 在每行用户上独立执行。如果漏掉 LEFT JOIN,没下过单的用户就直接消失了。
- 别在子查询里用
OFFSET 0或其他无意义修饰,部分引擎(如旧版 BigQuery)会因此禁用 LATERAL 优化 - 子查询若含聚合(如
AVG()),必须确保 GROUP BY 或窗口函数逻辑清晰,否则可能意外放大主表行数 - 性能敏感场景下,
ORDER BY + LIMIT必须有(user_id, order_time)复合索引,否则全表扫描代价极高
嵌套 JSON 或数组字段解析时,LATERAL 是唯一可行路径
当主表某列存的是 JSON 数组(如 tags JSON 字段),你想把每个 tag 拆成一行并关联原始记录,jsonb_array_elements()(PostgreSQL)或 UNNEST()(BigQuery)必须配合 LATERAL:
SELECT u.name, tag.value->>'name' AS tag_name FROM users u JOIN LATERAL jsonb_array_elements(u.tags) AS tag ON true;
这里 jsonb_array_elements(u.tags) 的输入依赖于每行 u.tags,没有 LATERAL 就无法绑定上下文。MySQL 用户只能靠 JSON_TABLE(8.0.22+)替代,但语法和语义完全不同。
- BigQuery 中对应写法是
UNNEST(u.tags) AS tag,无需显式写LATERAL(隐式支持),但习惯统一写CROSS JOIN UNNEST(...)更安全 - PostgreSQL 中若用
json_array_elements()(非 jsonb 版本),遇到 null 或非法 JSON 会报错,建议先用jsonb_path_exists()过滤 - 拆出来的字段若需进一步过滤(如只取
tag.type = 'feature'),务必写在子查询的WHERE里,而不是主查询,否则失去行级隔离性
LEFT JOIN LATERAL 与 JOIN LATERAL 的行为差异极易踩坑
很多人以为加了 LEFT 就万事大吉,其实 ON true 和 ON some_condition 效果天差地别。例如:
-- ✅ 正确:保留所有用户,即使子查询无结果 LEFT JOIN LATERAL (SELECT ...) AS x ON true <p>-- ❌ 错误:等价于 INNER JOIN,因为 ON 条件永远不成立(x 列不存在) LEFT JOIN LATERAL (SELECT ...) AS x ON x.id IS NOT NULL</p>
根本原因是 LATERAL 子查询的输出列在 ON 子句中不可见(作用域限制),所以 ON x.id IS NOT NULL 实际永远为 false。
- 只要想保留主表所有行,一律用
ON true,这是约定俗成的安全写法 - 如果子查询本身可能返回多行,又想控制匹配逻辑(比如只取第一行),应在子查询内用
LIMIT 1或窗口函数,而不是指望ON来筛 - 某些引擎(如 Trino)对
ON true有额外优化提示,但 PostgreSQL 不需要,写法统一即可
真正难的不是写对语法,而是意识到什么时候必须用 LATERAL:当你发现子查询里不得不写主表字段、又没法提前物化中间结果时,就是它的主场。其它时候,老老实实用 CTE 或临时表反而更清晰。










