lateral能替代相关子查询,因其将每行触发的dependent subquery转为按行求值的物化查找,避免10万订单触发10万次全表扫描;必须mysql 8.0.14+,仅限from子句使用,且需order_id与update_time的降序联合索引支撑。

LATERAL 不是“关联函数”,它是 FROM 子句中的关键字,用错位置或版本不匹配,查询直接报错或退化为慢查询。
为什么 LATERAL 能替代相关子查询
传统写法如 (SELECT status FROM logistics WHERE order_id = o.order_id ORDER BY update_time DESC LIMIT 1) 是 DEPENDENT SUBQUERY,EXPLAIN 显示每行订单都重跑一次子查询,10 万订单就执行 10 万次全表扫描。LATERAL 让子查询真正“按行求值”:优化器会把它转成物化临时表 + 索引查找,只对匹配的 order_id 执行一次查询。
- 必须 MySQL 8.0.14+,低版本(包括 8.0.13)不识别
LATERAL关键字,会报语法错误 - 子查询里可直接用
o.order_id,不用再写WHERE t.order_id = o.order_id - 返回 0 行时,外层该行被丢弃(类似
INNER JOIN),想保留得改用LEFT JOIN LATERAL
LATERAL 必须出现在 FROM 子句中
LATERAL 只能在 FROM 后、JOIN 右侧使用,不能放在 WHERE 或 SELECT 里。常见错误写法:SELECT ..., (LATERAL SELECT ...) 或 WHERE EXISTS LATERAL (...) —— 这两种都会触发 SQL 解析失败。
- 正确结构:
FROM orders o LEFT JOIN LATERAL (SELECT ... FROM logistics t WHERE t.order_id = o.order_id ...) AS t ON TRUE -
ON TRUE是占位写法,实际关联逻辑已移入子查询内部;若需额外过滤(如t.status = 'delivered'),应写在子查询的WHERE中,而非外部ON - 逗号隐式 JOIN 也支持:
FROM orders o, LATERAL (SELECT ... WHERE t.order_id = o.order_id) AS t,但可读性差,不推荐
性能陷阱:没索引,LATERAL 也救不了你
哪怕用了 LATERAL,如果子查询涉及的字段(如 logistics.order_id 和 logistics.update_time)没联合索引,仍会触发 Using filesort 或全表扫描。
- 必须建覆盖索引:
CREATE INDEX idx_order_time ON logistics (order_id, update_time DESC) - 注意降序索引仅 MySQL 8.0+ 支持,5.7 下
DESC会被忽略,排序仍走临时文件 - 子查询里用
LIMIT 1时,索引必须把排序字段放在第二位,否则无法跳过排序步骤
最易被忽略的是:LATERAL 的语义是“逐行触发”,但它不改变底层数据访问路径——索引是否生效、是否需要临时表、是否能避免 filesort,全取决于子查询自身的 WHERE 和 ORDER BY 是否能命中索引。写对语法只是第一步,索引设计才是性能分水岭。











