1. 覆盖索引的工作原理
当查询只涉及索引中已经包含的列时,SQL Server 可以直接使用索引来返回查询结果,而不需要回表到数据页去检索实际的数据行。覆盖索引因此能够显著减少 I/O 操作,提高查询效率。
例如,假设有一个表 Employees
,包含以下列:
CREATE TABLE Employees (id INT PRIMARY KEY,name VARCHAR(100),age INT,salary DECIMAL(10, 2)
);
如果你为 name
和 salary
列创建了一个复合索引:
CREATE INDEX idx_name_salary ON Employees(name, salary);
那么在以下查询中,SQL Server 将使用该覆盖索引:
SELECT name, salary FROM Employees WHERE name = 'John Doe';
因为查询只涉及 name
和 salary
,SQL Server 可以直接从索引中获取结果,而不需要访问表中的数据行。
2. 覆盖索引的创建
为了优化查询性能,SQL Server 提供了包括列(INCLUDE)的功能,允许你在索引中包含额外的列,这些列不会作为索引的排序依据,但会存储在索引叶子节点中,供查询时直接使用。
例如,假设你经常执行如下查询:
SELECT name, salary, age FROM Employees WHERE salary > 50000;
你可以创建一个索引,其中包括 name
和 salary
作为索引的排序列,并将 age
列作为“包括列”:
CREATE INDEX idx_salary_name IN Employees(salary, name)
INCLUDE (age);
此时,salary
和 name
列作为排序列,age
列作为包括列,这样 SQL Server 在执行查询时就能直接从索引中获取所有所需的列,避免了回表操作。
3. SQL Server 的覆盖索引优势
- 性能提升:避免回表,减少磁盘 I/O,查询结果可以直接从索引中返回,性能得到提升。
- 存储优化:合理使用 INCLUDE 子句,可以避免将所有查询字段都作为排序列,从而减少索引的大小。
4. 使用 INCLUDE 子句
在 SQL Server 中,INCLUDE
子句是优化覆盖索引的重要工具。它允许你在索引中包含额外的列,而不会影响索引的排序方式,且这些列仅用于覆盖查询。
例如,以下索引创建语句:
CREATE INDEX idx_name_salary INCLUDE (age);
该索引会包括 name
和 salary
作为排序列,并且在索引的叶子节点中包含 age
列。当查询涉及 name
、salary
和 age
时,SQL Server 会使用此索引覆盖查询,而不需要访问数据表。
5. 注意事项
- 索引大小:覆盖索引的大小可能会变得较大,尤其是当你包含多个列或大列时,因此要小心选择需要覆盖的列。
- 更新开销:如果索引涉及频繁更新的列,维护覆盖索引的开销可能会增加。
6. 例子总结
假设你有一个查询:
SELECT name, salary FROM Employees WHERE salary > 50000;
如果你创建如下的索引:
CREATE INDEX idx_salary_name ON Employees(salary, name);
那么这个查询将完全由覆盖索引处理,SQL Server 不需要回表。假如查询涉及更多列,例如:
SELECT name, salary, age FROM Employees WHERE salary > 50000;
那么你可以创建一个包含 age
列的索引:
CREATE INDEX idx_salary_name_age ON Employees(salary, name) INCLUDE (age);
在这种情况下,SQL Server 会使用该索引覆盖查询,不需要回表到 Employees
表来查找 age
列的数据。
总结
SQL Server 中的覆盖索引与其他数据库系统的概念非常相似,都是通过索引中包含查询所需的所有列来避免回表,从而提升查询性能。SQL Server 通过 INCLUDE
子句提供了更大的灵活性,使得覆盖索引的创建可以更加精细和高效。
创建覆盖索引的脚本与普通索引脚本是类似的,但有一个关键的区别:在创建覆盖索引时,通常会使用 INCLUDE
子句来指定额外的列,使索引包含查询所需的所有字段,而不影响索引的排序列。
1. 普通索引创建脚本
普通索引创建脚本通常只指定索引的排序列。例如:
CREATE INDEX idx_name_salary ON Employees(name, salary);
这个索引 idx_name_salary
是一个普通的非覆盖索引,它按 name
和 salary
列排序。如果查询只涉及 name
和 salary
,这个索引就能加速查询。但如果查询还需要其他列(比如 age
),SQL Server 仍然需要回表去查找这些列的数据。
2. 覆盖索引创建脚本
覆盖索引的创建脚本除了指定排序列外,还会使用 INCLUDE
子句将额外的列包含在索引的叶子节点中,这样可以避免回表。例如,如果你希望查询涉及 name
、salary
和 age
,你可以创建一个覆盖索引,包含所有需要的列:
CREATE INDEX idx_name_salary_age ON Employees(name, salary)
INCLUDE (age);
在这个例子中:
name
和salary
是排序列,它们决定了索引的组织方式。age
列是通过INCLUDE
子句包含在索引中的,它不会影响索引的排序方式,但是查询中需要age
时,SQL Server 会直接从索引中获取它,而无需回表去查找实际的数据。
3. 总结区别
- 普通索引:只有排序列,没有使用
INCLUDE
子句,适用于仅查询排序列的情况。 - 覆盖索引:使用
INCLUDE
子句将额外的列包含在索引中,适用于查询包含多个列时,避免回表。
示例比较
- 普通索引:
CREATE INDEX idx_name_salary ON Employees(name, salary);
- 覆盖索引:
CREATE INDEX idx_name_salary_age ON Employees(name, salary)
INCLUDE (age);
关键点
INCLUDE
子句只是用来将非排序列(查询需要的列)包括进索引叶子节点,以提高查询性能。- 在大多数情况下,只有当查询涉及多个列时,才使用覆盖索引。如果查询只涉及少数几列,使用普通索引可能更合适。
这样,覆盖索引的创建脚本和普通索引脚本的区别就是是否使用了 INCLUDE
子句。