客户的订单统计报表,数据量涨到 800 万行后打开要 40 秒,财务每月结账都要骂一次。优化后稳定在 0.3 秒。整个过程没有换数据库、没有加机器,只做对了三件事。
第一步:先看执行计划,不要猜
慢查询优化最大的误区是"凭感觉加索引"。正确姿势是打开实际执行计划(SSMS 里 Ctrl+M),找三样东西:
- 扫描(Scan)还是查找(Seek):800 万行的表出现 Clustered Index Scan,基本可以确定缺索引;
- 键查找(Key Lookup):索引覆盖了过滤条件但没覆盖查询列,回表次数巨大;
- 预估行数 vs 实际行数:差几个数量级说明统计信息过期,优化器在用过期地图导航。
这个案例的执行计划里,三样全中。
第二步:建"覆盖索引",一次到位
报表 SQL 按"日期范围 + 客户状态"过滤,统计金额汇总。我们建的索引:
- 键列放过滤列(OrderDate, Status),顺序把等值条件放前面、范围条件放后面;
- 用 INCLUDE 把汇总用到的列(Amount, Quantity)挂进索引页,避免回表;
- 建完更新统计信息:
UPDATE STATISTICS。
索引建完,同样的查询从 40 秒降到 1.2 秒——执行计划里 Scan 变 Seek,Key Lookup 消失。
第三步:改写剩下的坏味道
1.2 秒还不够,瓶颈变成了一个标量函数:SELECT 列表里有个对每行执行的日期格式化函数,800 万次调用。改成预计算列 + 索引后,0.3 秒收工。
顺手清单,慢查询常见的几个写法问题:
WHERE YEAR(OrderDate)=2026—— 对列做函数运算,索引直接失效,改写成范围比较;NOT IN (子查询)—— 子查询有 NULL 时结果全空,改用NOT EXISTS;SELECT *—— 拉回一堆用不到的列,索引覆盖无从谈起。
一句话总结
慢查询优化 = 执行计划定位 + 覆盖索引 + 去掉对列的函数运算。这套组合拳能解决企业系统里九成的性能问题,而且一分钱硬件都不用加。