SQLSERVER查询优化

数据库优化的难点从来不在“知道概念”,而在于“如何实践”。这篇文章不讲虚的,每个优化方式都提供:

  • 问题 SQL(低性能)
  • 优化后的 SQL(高性能)
  • 使用到的索引
  • 原因解释

无论是订单系统、后台管理系统还是 BI 统计,都能直接套用。


一、避免全表扫描:为过滤字段建立合适的索引

❌ 问题示例:WHERE 条件无索引导致 Table Scan

SELECT OrderId, UserId, Amount
FROM Orders
WHERE UserId = 10001;

如果 UserId 没有索引,执行计划会是 Table Scan,随着数据量增加性能急剧下降。

✅ 解决方式:增加非聚集索引

CREATE INDEX IX_Orders_UserId
ON Orders(UserId);

🚀 优化后的查询

SELECT OrderId, UserId, Amount
FROM Orders
WHERE UserId = 10001;

SQL Server 会使用 Index Seek(高性能),查询速度从毫秒级下降到微秒级。


二、避免 Key Lookup:使用包含列让索引“覆盖查询”

❌ 问题示例:索引字段不够,导致大量回表

SELECT OrderId, UserId, Amount
FROM Orders
WHERE UserId = 10001;

如果索引只有 UserId,那 SQL Server 会:

  1. Seek 找出 UserId 对应记录
  2. 回表(Lookup)用于读取 Amount 字段

大量回表会造成严重 IO 开销。

❌ 原有索引

CREATE INDEX IX_Orders_UserId
ON Orders(UserId);

✅ 解决方式:使用 INCLUDE 构建覆盖索引

CREATE INDEX IX_Orders_UserId
ON Orders(UserId)
INCLUDE (OrderId, Amount);

🚀 优化后的效果

查询完全在索引内完成,不再回表,执行计划 Key Lookup 消失。


三、避免在字段上使用函数:保持可 SARGABLE(可利用索引)

❌ 问题示例:对字段使用函数,导致索引失效

SELECT *
FROM Orders
WHERE CONVERT(VARCHAR(10), CreateTime, 120) = '2025-12-10';

执行计划会出现:

  • Index Scan
  • 甚至 Table Scan

因为 SQL Server 无法使用时间字段上的索引。

❌ 原有索引

CREATE INDEX IX_Orders_CreateTime
ON Orders(CreateTime);

✅ 优化方式:改用范围查询

SELECT *
FROM Orders
WHERE CreateTime >= '2025-12-10'
  AND CreateTime <  '2025-12-11';

此时索引恢复有效,使用 Index Seek


四、避免 SELECT *:减少 IO,提升覆盖率

❌ 问题示例:滥用 SELECT * 导致性能下降

SELECT *
FROM Orders
WHERE Status = 1;

问题:

  • 改变返回列会改变执行计划
  • 无法利用覆盖索引
  • IO 成本高

❌ 原有索引

CREATE INDEX IX_Orders_Status
ON Orders(Status);

当你 SELECT * 时,需要返回所有字段 ↓
索引无法覆盖,因此仍然会回表/扫描。

✅ 优化方式:只选择必要字段

SELECT OrderId, Status, Amount, CreateTime
FROM Orders
WHERE Status = 1;

并可优化索引:

CREATE INDEX IX_Orders_Status
ON Orders(Status)
INCLUDE(OrderId, Amount, CreateTime);

五、避免 OR 导致扫描:改写为 UNION ALL

❌ 问题 SQL:OR 条件让优化器放弃索引

SELECT *
FROM Orders
WHERE Status = 1 OR Status = 2;

这种 OR 通常导致 Index Scan

❌ 原有索引

CREATE INDEX IX_Orders_Status
ON Orders(Status);

尽管有索引,OR 会导致扫描。

✅ 优化方式:拆成两个查询 + UNION ALL

SELECT * FROM Orders WHERE Status = 1
UNION ALL
SELECT * FROM Orders WHERE Status = 2;

现在每个子查询都能使用 Index Seek。


六、JOIN 字段必须建立索引

❌ 问题示例:缺少 JOIN 字段索引 → Hash Join(昂贵)

SELECT o.OrderId, u.UserName
FROM Orders o
JOIN Users u ON o.UserId = u.Id;

如果:

  • Orders.UserId 无索引
  • Users.Id 无索引

那 SQL Server 只能使用 Hash Join

❌ 没有索引

-- 无索引

✅ 解决:为 JOIN 字段增加索引

CREATE INDEX IX_Orders_UserId ON Orders(UserId);
CREATE INDEX IX_Users_Id ON Users(Id);

🚀 优化后

执行计划从 Hash Join 变为 Nested Loop,大幅降低 CPU 和内存使用。


七、利用统计信息提高计划质量

❌ 问题示例:统计信息陈旧导致优化器估算错误

SELECT *
FROM Orders
WHERE Amount > 1000;

如果数据大量新增/修改,优化器的估算行数(Estimated Rows)会完全错误,导致选择错误索引或错误 JOIN 类型。

❌ 表现

  • Hash Join 出现
  • 扫描出现
  • Key Lookup 激增

✅ 解决方式:更新统计信息

UPDATE STATISTICS Orders WITH FULLSCAN;

或启用自动更新(推荐)

ALTER DATABASE db SET AUTO_UPDATE_STATISTICS ON;

八、处理参数嗅探:OPTION (RECOMPILE) 与本地变量技巧

❌ 问题示例:参数嗅探导致执行计划“偏向某个值”

例子:

CREATE PROCEDURE GetOrders
    @UserId INT
AS
BEGIN
    SELECT *
    FROM Orders
    WHERE UserId = @UserId;
END

如果首次执行传入一个极小结果集,会缓存“错误的轻量计划”,导致大用户的数据查询变慢。

解决方式一:强制重新生成执行计划

SELECT *
FROM Orders
WHERE UserId = @UserId
OPTION (RECOMPILE);

解决方式二:使用本地变量,让 SQL Server 不复用计划

DECLARE @uid INT = @UserId;

SELECT *
FROM Orders
WHERE UserId = @uid;

九、索引碎片影响性能:定期 Rebuild/Reorganize

❌ 问题示例:大量插入/删除导致索引碎片

SELECT *
FROM Orders
WHERE UserId = 20001;

即使有索引,也可能因碎片过高导致性能下降。

查看碎片率

SELECT index_id, avg_fragmentation_in_percent
FROM sys.dm_db_index_physical_stats(DB_ID(), OBJECT_ID('Orders'), NULL, NULL, NULL);

⭐ 优化方式

ALTER INDEX ALL ON Orders REBUILD;     -- 碎片 > 30%
ALTER INDEX ALL ON Orders REORGANIZE;  -- 10% ~ 30%

总结:查询优化的核心是“让 SQL Server 更容易做正确决定”

每一种优化方式最终都是为了一个目标:

让 SQL Server 的执行计划更准确、索引更高效,避免无意义的扫描和昂贵操作。

本篇文章给出的每一个优化都有:

  • 慢 SQL 示例
  • 产生慢的原因
  • 对应索引和写法
  • 优化后的高性能 SQL
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇