PostgreSQL 如何用 LATERAL 子查询替换相关子查询提升性能
发布时间 - 2026-01-30 00:00:00 点击率:次相关子查询在PostgreSQL中易变慢,因其每行外层数据都重执行内层查询,若无索引或复杂JOIN,性能呈指数恶化;LATERAL可显式声明依赖、支持下推过滤与更优连接算法。
为什么相关子查询在 PostgreSQL 里容易变慢
相关子查询(correlated subquery)每次外层行都会重新执行一次内层查询,如果外层有 10 万行,内层又没走索引或涉及多表 JOIN,性能会指数级恶化。PostgreSQL 无法对多数相关子查询做有效物化,优化器常被迫选择嵌套循环(Nested Loop),而 LATERAL 显式声明依赖关系后,优化器能更早识别可下推的过滤条件、复用索引扫描,甚至改用 Hash Join 或 Merge Join。
用 LATERAL 替换单值标量子查询的写法
常见场景是“查每个用户最新一条订单”:
SELECT u.id, u.name, (SELECT o.amount FROM orders o WHERE o.user_id = u.id ORDER BY o.created_at DESC LIMIT 1) AS last_amount FROM users u;
改成 LATERAL 后:
SELECT u.id, u.name, l.amount AS last_amount FROM users u LEFT JOIN LATERAL ( SELECT o.amount FROM orders o WHERE o.user_id = u.id ORDER BY o.created_at DESC LIMIT 1 ) l ON true;
-
LATERAL子查询可直接引用u.id,且只对当前u行执行一次 -
LEFT JOIN ... ON true保证用户行不丢失(对应原标量子查询返回 NULL 的行为) - 必须给子查询起别名(如
l),否则语法报错:ERROR: syntax error at or near "SELECT" - 若确定每用户必有订单,可用
JOIN LATERAL省去ON true
替换多列/多行结果时要注意 JOIN 类型和别名作用域
当原相关子查询返回多列或多行(如每个用户最近 3 笔订单),LATERAL 更自然:
SELECT u.id, u.name, o.id AS order_id, o.amount, o.created_at FROM users u JOIN LATERAL ( SELECT id, amount, created_at FROM orders o2 WHERE o2.user_id = u.id ORDER BY o2.created_at DESC LIMIT 3 ) o ON true;
- 不能写成
SELECT * FROM users u, LATERAL (…)—— 虽然语法允许,但语义模糊,易误读为 CROSS JOIN -
LATERAL子查询里的表别名(如o2)不能和外层同名,否则报错:table name "o2" specified more than once - 若子查询可能无结果,又想保留用户行,必须用
LEFT JOIN LATERAL,且ON true不可省略 - 子查询中
ORDER BY + LIMIT若缺索引,仍会慢;确保(user_id, created_at DESC)有联合索引
哪些情况 LATERAL 反而更差?
不是所有相关子查询都适合换。以下情形要警惕:
- 子查询逻辑极简单(如
SELECT u.id * 2),LATERAL增加解析开销,无实际收益 - 外层结果集极小(LATERAL 不改变执行次数,只是让计划更“显式”,未必提速
- 子查询含不可下推的聚合或窗口函数(如
ROW_NUMBER() OVER (PARTITION BY ...)),LATERAL无法规避重复计算
使用了
UNION或多个CTE嵌套的复杂子查询,LATERAL可能导致计划退化,需对比EXPLAIN (ANALYZE, BUFFERS)
真正关键的是:先看执行计划里是否出现高频的 Subplan 节点,再测 LATERAL 版本——它不自动加速,只是给了优化器更清晰的优化路径。索引缺失时,换写法也救不了性能。
# 作用域
# 为什么
# sql
# NULL
# select
# Error
# 循环
# table
# 算法
# postgresql
# 报错
# 变慢
# 的是
# 多个
# 误读
# 给了
# 可直接
# 必有
# 它不
# 时要
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
如何在万网开始建站?分步指南解析
利用JavaScript实现拖拽改变元素大小
zabbix利用python脚本发送报警邮件的方法
如何撰写建站申请书?关键要点有哪些?
JS中对数组元素进行增删改移的方法总结
Laravel如何生成API文档?(Swagger/OpenAPI教程)
深圳防火门网站制作公司,深圳中天明防火门怎么编码?
node.js报错:Cannot find module 'ejs'的解决办法
Python图片处理进阶教程_Pillow滤镜与图像增强
移动端脚本框架Hammer.js
php json中文编码为null的解决办法
如何正确选择百度移动适配建站域名?
javascript基于原型链的继承及call和apply函数用法分析
Laravel如何集成第三方登录_Laravel Socialite实现微信QQ微博登录
Laravel PHP版本要求一览_Laravel各版本环境要求对照
手机网站制作与建设方案,手机网站如何建设?
Laravel如何实现多对多模型关联?(Eloquent教程)
重庆市网站制作公司,重庆招聘网站哪个好?
JavaScript模板引擎Template.js使用详解
Laravel DB事务怎么使用_Laravel数据库事务回滚操作
如何在橙子建站中快速调整背景颜色?
Win11怎么开启自动HDR画质_Windows11显示设置HDR选项
什么是JavaScript解构赋值_解构赋值有哪些实用技巧
C++用Dijkstra(迪杰斯特拉)算法求最短路径
如何快速生成凡客建站的专业级图册?
Laravel N+1查询问题如何解决_Eloquent预加载(Eager Loading)优化数据库查询
网站制作大概要多少钱一个,做一个平台网站大概多少钱?
在线制作视频的网站有哪些,电脑如何制作视频短片?
高防服务器租用指南:配置选择与快速部署攻略
Win11怎么更改系统语言为中文_Windows11安装语言包并设为显示语言
黑客如何利用漏洞与弱口令入侵网站服务器?
nodejs redis 发布订阅机制封装实现方法及实例代码
Laravel怎么防止CSRF攻击_Laravel CSRF保护中间件原理与实践
如何快速搭建高效WAP手机网站吸引移动用户?
广州网站制作公司哪家好一点,广州欧莱雅百库网络科技有限公司官网?
青岛网站建设如何选择本地服务器?
Laravel怎么导出Excel文件_Laravel Excel插件使用教程
手机怎么制作网站教程步骤,手机怎么做自己的网页链接?
Bootstrap整体框架之JavaScript插件架构
用v-html解决Vue.js渲染中html标签不被解析的问题
Laravel如何使用Passport实现OAuth2?(完整配置步骤)
Laravel的Blade指令怎么自定义_创建你自己的Laravel Blade Directives
Laravel怎么设置路由分组Prefix_Laravel多级路由嵌套与命名空间隔离【步骤】
Python文件操作最佳实践_稳定性说明【指导】
北京的网站制作公司有哪些,哪个视频网站最好?
Laravel怎么做数据加密_Laravel内置Crypt门面的加密与解密功能
JavaScript如何实现继承_有哪些常用方法
教你用AI润色文章,让你的文字表达更专业
INTERNET浏览器怎样恢复关闭标签页_INTERNET浏览器标签恢复快捷键与方法【指南】
Laravel API路由如何设计_Laravel构建RESTful API的路由最佳实践


