0

0

SQL 如何判断一条查询是否“过复杂”?

舞夢輝影

舞夢輝影

发布时间:2026-01-25 08:26:02

|

241人浏览过

|

来源于php中文网

原创

最常被忽略的性能雷区是Nested Loop驱动大表,即百万级表出现在内层被逐行扫描,常见于缺失索引、函数包裹ON条件或OR拆分连接;需检查Rows Removed by Filter、logical_reads超10万页、type: ALL非驱动表、JOIN超5个跨域关联、统计信息陈旧致驱动顺序错误、非等值连接、三层嵌套聚合内存爆满、函数/CAST使索引失效等。

sql 如何判断一条查询是否“过复杂”?

看执行计划里有没有 Nested Loop 驱动大表

这是最常被忽略的性能雷区。当 EXPLAIN(或 EXPLAIN ANALYZE)显示某张行数上百万的表出现在 Nested Loop 的内层(即被外层结果集逐行驱动),哪怕只查 10 行,实际也可能扫描千万级记录。

常见诱因包括:缺失连接字段索引、ON 条件写成函数包裹(如 ON UPPER(a.name) = UPPER(b.name))、用 OR 拆分连接条件导致优化器放弃哈希/合并连接。

  • 检查 EXPLAIN 输出中 Rows Removed by Filter 是否远高于 Actual Rows —— 这说明大量中间结果被丢弃,过滤逻辑没下推
  • pg_stat_statements(PostgreSQL)或 sys.dm_exec_query_stats(SQL Server)查历史平均 logical_reads,单次查询超 10 万页读基本算过复杂
  • MySQL 用户注意:type: ALL 出现在 EXPLAIN 的非驱动表上,基本等于“正在全表扫”

JOIN 数量超过 5 个且无明确业务边界

不是语法报错,但意味着查询责任模糊、变更风险高、缓存失效快。尤其当多个 JOIN 涉及不同业务域(比如同时连订单、库存、物流、用户画像、营销活动),任何一端数据模型微调都可能让整个查询崩掉或返回错误聚合结果。

更隐蔽的问题是:优化器在多表关联时容易选错驱动顺序,尤其当统计信息陈旧(ANALYZE 没跑过)或存在 LEFT JOIN + WHERE 字段来自右表时,会隐式转成 INNER JOIN,结果集意外缩水。

  • 跨域关联拆成应用层多次查询,用主键集合做二次匹配(例如先查订单 ID 列表,再用 IN (…) 查对应用户标签)
  • 确认每个 JOIN 是否真需要:用 EXISTS 替代 LEFT JOIN … WHERE xxx IS NULL 常能砍掉一个表
  • 避免在 ON 子句里混用 =!= 或范围条件(如 a.id > b.ref_id),这类非等值连接几乎必然触发嵌套循环

子查询嵌套深度 ≥ 3 层且含聚合或窗口函数

三层嵌套本身不致命,但若最内层有 COUNT(*) OVER (PARTITION BY x),中间层又用 GROUP BY,外层再套 ORDER BY … LIMIT,数据库就得把中间结果全算出来才能裁剪——内存爆掉、临时磁盘写满都是常态。

腾讯AI 开放平台
腾讯AI 开放平台

腾讯AI开放平台

下载

典型症状是 EXPLAIN 显示 Materialize 节点出现在关键路径上,且估算行数和实际行数偏差超 10 倍(说明统计信息严重失真,优化器误判)。

  • 把最内层聚合结果物化成临时表(CREATE TEMP TABLE AS SELECT …),显式控制生命周期
  • 用 CTE(WITH)替代子查询时,注意 PostgreSQL 会强制物化,而 MySQL 8.0+ 默认不物化——别假设行为一致
  • 窗口函数尽量靠近最终输出层,避免在子查询里提前计算再被外层过滤掉(如 ROW_NUMBER() 算了 100 万行,最后只取第 1 行)

WHERE 条件里出现 CAST、函数或表达式作用于索引字段

比如 WHERE DATE(created_at) = '2024-01-01'WHERE SUBSTR(phone, 1, 3) = '138',会导致索引完全失效。优化器只能全表扫描,而你可能根本没意识到——因为 EXPLAIN 只说 Seq Scan,不告诉你“本可以走索引但被你写废了”。

更麻烦的是隐式转换:字符串字段存数字(status VARCHAR(10)),却写成 WHERE status = 1,MySQL 会把整列转为数字比对;SQL Server 在某些兼容模式下也会干类似的事。

  • 索引字段必须裸写:改成 WHERE created_at >= '2024-01-01' AND created_at
  • 字符串字段查数字?统一转成字符串比较:WHERE status = '1',并确保字段类型和值类型严格一致
  • pg_indexes(PostgreSQL)或 sp_helpindex(SQL Server)确认索引是否真被创建在目标列上,别信名字——idx_user_created 可能建在 updated_at

真正难判断的,从来不是“语法有多长”,而是“哪一行改动会让响应时间从 200ms 暴涨到 12s”。盯住执行计划里的实际扫描行数、物化节点位置、索引使用状态,比数 JOIN 个数有用得多。

相关专题

更多
数据分析工具有哪些
数据分析工具有哪些

数据分析工具有Excel、SQL、Python、R、Tableau、Power BI、SAS、SPSS和MATLAB等。详细介绍:1、Excel,具有强大的计算和数据处理功能;2、SQL,可以进行数据查询、过滤、排序、聚合等操作;3、Python,拥有丰富的数据分析库;4、R,拥有丰富的统计分析库和图形库;5、Tableau,提供了直观易用的用户界面等等。

686

2023.10.12

SQL中distinct的用法
SQL中distinct的用法

SQL中distinct的语法是“SELECT DISTINCT column1, column2,...,FROM table_name;”。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

324

2023.10.27

SQL中months_between使用方法
SQL中months_between使用方法

在SQL中,MONTHS_BETWEEN 是一个常见的函数,用于计算两个日期之间的月份差。想了解更多SQL的相关内容,可以阅读本专题下面的文章。

348

2024.02.23

SQL出现5120错误解决方法
SQL出现5120错误解决方法

SQL Server错误5120是由于没有足够的权限来访问或操作指定的数据库或文件引起的。想了解更多sql错误的相关内容,可以阅读本专题下面的文章。

1137

2024.03.06

sql procedure语法错误解决方法
sql procedure语法错误解决方法

sql procedure语法错误解决办法:1、仔细检查错误消息;2、检查语法规则;3、检查括号和引号;4、检查变量和参数;5、检查关键字和函数;6、逐步调试;7、参考文档和示例。想了解更多语法错误的相关内容,可以阅读本专题下面的文章。

359

2024.03.06

oracle数据库运行sql方法
oracle数据库运行sql方法

运行sql步骤包括:打开sql plus工具并连接到数据库。在提示符下输入sql语句。按enter键运行该语句。查看结果,错误消息或退出sql plus。想了解更多oracle数据库的相关内容,可以阅读本专题下面的文章。

737

2024.04.07

sql中where的含义
sql中where的含义

sql中where子句用于从表中过滤数据,它基于指定条件选择特定的行。想了解更多where的相关内容,可以阅读本专题下面的文章。

577

2024.04.29

sql中删除表的语句是什么
sql中删除表的语句是什么

sql中用于删除表的语句是drop table。语法为drop table table_name;该语句将永久删除指定表的表和数据。想了解更多sql的相关内容,可以阅读本专题下面的文章。

420

2024.04.29

c++ 根号
c++ 根号

本专题整合了c++根号相关教程,阅读专题下面的文章了解更多详细内容。

25

2026.01.23

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
MySQL 教程
MySQL 教程

共48课时 | 1.9万人学习

MySQL 初学入门(mosh老师)
MySQL 初学入门(mosh老师)

共3课时 | 0.3万人学习

简单聊聊mysql8与网络通信
简单聊聊mysql8与网络通信

共1课时 | 810人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号