SQL Server实用索引创建方法及JOIN查询优化问题咨询
针对你的SQL查询优化与实用索引创建指南
首先,咱们先拆解下你的问题:为什么你创建的索引没被执行计划选中,以及怎么优化这个查询、创建真正有用的索引。
一、为什么你的索引没生效?
你创建的index_dept3和index_branch3都是单列非聚集索引,但没被用上大概率是这两个原因:
- 表数据量太小:如果
Employee表只有几百条甚至更少的数据,SQL Server的查询优化器会认为全表扫描比走索引更高效——毕竟索引本身也需要额外IO去定位数据,小表全扫的成本更低。你可以往表里插入几万条测试数据再观察执行计划。 - 索引覆盖度不足:你的查询需要返回
Emp_Id、First_Name等多个列,而单列索引只存储了Dept_Id/ASSIGNED_BRANCH_ID和对应的行指针。SQL Server如果走这个索引,还得回表去查其他需要的列(也就是「书签查找」),当回表的成本超过全表扫描时,优化器就会放弃使用这个索引。
二、针对当前查询的优化方案
针对你的连接查询,最有效的方式是创建覆盖索引,让索引本身就能提供查询需要的所有数据,避免回表操作:
1. 给Employee表创建包含必要列的覆盖索引
-- 针对与Department连接的覆盖索引 CREATE INDEX IX_Employee_DeptId_Included ON Employee (Dept_Id) INCLUDE (Emp_Id, First_Name, Last_Name, Title, ASSIGNED_BRANCH_ID); -- 针对与Branch连接的覆盖索引 CREATE INDEX IX_Employee_AssignedBranchId_Included ON Employee (ASSIGNED_BRANCH_ID) INCLUDE (Emp_Id, First_Name, Last_Name, Title, Dept_Id);
这里把连接列作为索引键,把查询需要返回的其他列放到INCLUDE里——这样索引键足够小,同时又能覆盖所有查询需求,优化器大概率会选择使用这些索引。
2. 确保关联表的连接列有索引
如果Department.Dept_Id和Branch.BRANCH_ID不是主键(主键默认会创建聚集索引),那也要给它们创建包含返回列的索引:
-- 给Department表创建索引(如果Dept_Id不是主键) CREATE INDEX IX_Department_DeptId ON Department (Dept_Id) INCLUDE (Name); -- 给Branch表创建索引(如果BRANCH_ID不是主键) CREATE INDEX IX_Branch_BranchId ON Branch (BRANCH_ID) INCLUDE (NAME);
3. 辅助操作:更新统计信息
如果你的表数据有过大量变更,SQL Server的统计信息可能过时,导致优化器做出错误判断。可以执行以下命令更新统计信息:
UPDATE STATISTICS Employee; UPDATE STATISTICS Department; UPDATE STATISTICS Branch;
三、SQL Server创建实用索引的核心原则
1. 优先做覆盖索引
这是最能提升查询性能的方式:让索引包含查询所需的所有列——索引键放WHERE筛选列、JOIN连接列、ORDER BY/GROUP BY列,INCLUDE放需要返回的其他列。这样完全避免回表,性能提升明显。
2. 贴合查询模式设计索引
不要盲目给每个列建索引,先分析你的常用查询:
- 哪些列是用来筛选数据的?(放索引键)
- 哪些列是用来连接其他表的?(放索引键)
- 哪些列是需要最终返回的?(放
INCLUDE)
3. 区分聚集索引与非聚集索引
- 聚集索引:一个表只能有一个,它决定了表数据的物理存储顺序,通常选唯一、稳定、查询频繁的列(比如主键)。
- 非聚集索引:可以有多个,是基于聚集索引的指针(如果是堆表就是RID),所以聚集索引的选择会影响所有非聚集索引的性能。
4. 避免过度索引
索引会增加写入操作(INSERT/UPDATE/DELETE)的成本——每写一条数据,所有相关索引都要同步更新。所以只给高频查询创建必要的索引,不要贪多。
5. 用执行计划验证索引效果
创建索引后,一定要查看实际执行计划(SSMS按Ctrl+M),确认索引被用上了。如果没被用,再排查:是数据量太小?还是索引设计不符合查询模式?或是统计信息过时?
内容的提问来源于stack exchange,提问作者sun_say




