首页/数据/SQL 速查/LATERAL:子查询引用左侧表

LATERAL:子查询引用左侧表

让 FROM 中的子查询引用同一 FROM 里写在它左侧的表(类似逐行相关子查询):PostgreSQL 9.3+、MySQL 8.0.14+ 支持。

SQL 速查数据更新于 2026-10-13

LATERAL 是什么

LATERAL 让 FROM 里的子查询可以引用同一 FROM 中写在它左侧的表,效果类似「逐行的相关子查询」,区别是它能返回多列甚至多行。典型用途是「每组取最新一条并带上该行的其他字段」——标量子查询做不到返回多列,普通 JOIN 又要额外绕一圈。PostgreSQL 9.3+ 与 MySQL 8.0.14+ 支持,MySQL 只允许 LATERAL 用在派生表位置,范围比 PostgreSQL 窄;另一种等效思路是窗口函数 ROW_NUMBER。

基本信息

适用平台PostgreSQL 9.3+ / MySQL 8.0.14+
语义FROM 中的子查询可以引用它左侧的表
MySQL 限制只支持 LATERAL 派生表,不能用于函数调用等其他位置
替代方案窗口函数 ROW_NUMBER,一次扫描后筛选
性能关键内层查询要走索引,否则逐行执行的代价很高

典型使用场景

  • 每组取最新一条记录并带上该行的其他列
  • 明细行附带该组的前 N 条子记录
  • 需要引用外层列做相关计算的展开查询
  • 与 JSON 数组展开配合,逐行展开并关联
  • 替换返回多列的标量子查询写法
  • 与返回集合的函数配合:这类函数写在 FROM 里已隐含 LATERAL 语义

PostgreSQL 写法

PostgreSQL 写法
SELECT u.id, u.name, o.amount, o.created_at
FROM users u
CROSS JOIN LATERAL (
    SELECT amount, created_at
    FROM orders o
    WHERE o.user_id = u.id
    ORDER BY o.created_at DESC, o.id DESC
    LIMIT 1
) o;

MySQL 写法与窗口函数替代

MySQL 写法与窗口函数替代
-- MySQL:LATERAL 只能放在 FROM 的派生表位置
SELECT u.id, u.name, o.amount, o.created_at
FROM users u,
LATERAL (
    SELECT amount, created_at
    FROM orders o
    WHERE o.user_id = u.id
    ORDER BY o.created_at DESC, o.id DESC
    LIMIT 1
) o;

-- 等效思路:窗口函数一次扫描后过滤行号
SELECT id, user_id, amount, created_at
FROM (
  SELECT id, user_id, amount, created_at,
         ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC, id DESC) AS rn
  FROM orders
) t WHERE t.rn = 1;

LATERAL 与窗口函数对比

对比项LATERAL窗口函数 ROW_NUMBER
执行方式外层每行执行一次内层一次扫描后统一编号
索引利用必须走索引才高效需要排序,可能额外排序
写法可读性接近「每组取一条」的直觉需包一层派生表过滤行号
适用场景每组取少量列、内层条件复杂大结果集统一编号与筛选
PostgreSQL 里子查询要引用左侧表必须显式写 LATERAL 或 CROSS JOIN LATERAL;而返回集合的函数(如 jsonb_array_elements)写在 FROM 中时隐含 LATERAL 语义,可以直接引用左侧的列。
LATERAL 子查询通常逐行执行,外层行数大且内层没有可用索引时会明显变慢;先用 EXPLAIN 与窗口函数写法对比,确认哪个更划算再定稿。

易错点

  • 内层没有可用索引,LATERAL 逐行执行退化成逐行全表扫描
  • 以为 MySQL 的 LATERAL 与 PostgreSQL 能力一致,把它写到非派生表位置导致语法报错
  • 用 LATERAL 取每组一条但排序键不唯一,结果不稳定
  • 外层行数大时不与窗口函数写法对比计划,错过一次扫描完成的更优方案

数据来源与参考

  • MySQL 8.0 Reference Manual · Lateral Derived Tables
  • PostgreSQL Documentation · LATERAL Subqueries
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌