LATERAL JOIN 不是语法糖,而是唯一支持在右表子查询中安全引用左表字段的机制;普通 JOIN 会提前固化右表数据,无法动态生成行;PostgreSQL 和 MySQL 8.0.14+ 原生支持,需显式书写且子查询中不可将左表列用于 FROM 列表。

LATERAL JOIN 不是语法糖,它是唯一能让你在 JOIN 条件里安全引用左表字段并执行子查询的机制——不加 LATERAL,多数数据库会直接报错或返回意外空结果。
为什么普通 JOIN 无法替代 LATERAL?
当你需要对左表每一行“动态生成右表数据”(比如拆分 JSON、调用函数、分页查关联记录),普通 JOIN 会在整个查询开始前就固化右表数据集,无法感知左表当前行的值。
常见错误现象:ERROR: invalid reference to FROM-clause entry for table "t1"(PostgreSQL)、Unknown column 't1.id' in 'where clause'(MySQL 8.0.14+ 支持 LATERAL 但误写为普通 JOIN 时)。
- MySQL 8.0.14+ 和 PostgreSQL 9.3+ 原生支持
LATERAL;SQLite、SQL Server 不支持 - 即使支持,
LATERAL必须显式写出,不能省略——写成JOIN (SELECT ...)就是普通 JOIN,不是 LATERAL - 右表子查询中可直接引用左表别名(如
t1.user_id),但不可出现在子查询的FROM列表里(只能在WHERE/ON或标量子查询中)
PostgreSQL 中用 LATERAL 拆解 JSON 数组
场景:用户表 users 有个 preferences JSONB 字段,存着多个偏好项,需展开为多行并与偏好定义表 preference_types 关联。
SELECT u.id, u.name, p.type_name, lp.value FROM users u LEFT JOIN LATERAL ( SELECT elem->>'type' AS type_key, elem->>'value' AS value FROM jsonb_array_elements(u.preferences) AS elem ) AS lp ON true LEFT JOIN preference_types p ON p.key = lp.type_key;
关键点:
-
jsonb_array_elements(u.preferences)依赖u.preferences,必须用LATERAL才合法 -
ON true是惯用写法,表示无条件展开;若需过滤(如只取 value 不为空),写在子查询WHERE里更高效 - 避免在子查询中对
u做聚合或窗口函数——LATERAL 子查询按行执行,无法跨行访问
MySQL 中模拟 LATERAL 的等效写法(8.0.14+)
MySQL 要求 LATERAL 必须紧跟在 JOIN 后,且子查询不能是派生表(即不能带括号包裹整个 SELECT),否则解析失败。
错误写法:JOIN LATERAL (SELECT ... FROM t2 WHERE t2.ref_id = t1.id) → 报错 Unexpected '('
正确写法:
SELECT t1.name, t2.title FROM articles t1 JOIN LATERAL ( SELECT title FROM comments WHERE comments.article_id = t1.id ORDER BY created_at DESC LIMIT 3 ) AS t2;
- 子查询外层不能加括号;
LATERAL是关键字,不是函数,不加括号 - MySQL 不支持在 LATERAL 子查询中使用参数化变量(如
@var),只认左表列 - 性能敏感时,确保
comments.article_id有索引——LATERAL 会为左表每行单独执行一次子查询
真正容易被忽略的是:LATERAL 子查询的执行计划在 EXPLAIN 中表现为“per-loop”,意味着如果左表有 10 万行,而你没加 LIMIT 或 WHERE 过滤,右表可能被扫描 10 万次。别只盯着语法对不对,先看执行代价。










