SQL Server 2019入门学习教程,从入门到精通,SQL Server 2019 创建和使用索引 — 语法知识点及使用方法详解(12)

SQL Server 2019 创建和使用索引 --- 语法知识点及使用方法详解


一、索引的含义和特点

1. 含义

索引是数据库中一种特殊的查找表,用于加速对数据表中数据的检索。它类似于书籍的目录,通过建立关键字与数据物理位置的映射,提高查询效率。

2. 特点

  • 加快查询速度:特别是 WHERE、JOIN、ORDER BY 等操作。
  • 降低写入性能:INSERT、UPDATE、DELETE 操作需维护索引结构。
  • 占用存储空间:索引本身需要磁盘空间。
  • 自动维护:SQL Server 在数据变更时自动更新索引。
  • 可选择性创建:并非所有列都需要索引。

二、索引的分类

1. 聚集索引(Clustered Index)

  • 特点:决定表中数据的物理存储顺序;一个表只能有一个聚集索引。
  • 适用:常用于主键或经常范围查询的列。

2. 非聚集索引(Nonclustered Index)

  • 特点:独立于数据行的结构,包含索引键值和指向数据行的指针;一个表可建多个。
  • 适用:用于频繁查询但不修改的列。

3. 唯一索引(Unique Index)

  • 特点:确保索引列无重复值,可用于实施 UNIQUE 约束。

4. 包含列索引(Index with Included Columns)

  • 特点:非键列包含在叶级别,用于覆盖查询,避免书签查找。

5. 筛选索引(Filtered Index)

  • 特点:对表中部分行建立索引,提高查询性能并减少索引维护开销。

6. 全文索引(Full-Text Index)

  • 特点:用于对文本数据进行关键字搜索,支持模糊匹配。

7. XML 索引(XML Index)

  • 特点:用于加速 XML 数据类型的查询。

8. 空间索引(Spatial Index)

  • 特点:用于 geography 和 geometry 数据类型的空间查询。

三、索引的设计原则

  1. 高频查询列优先建索引:WHERE、JOIN、ORDER BY、GROUP BY 中的列。
  2. 高选择性列优先:如身份证号、邮箱等重复值少的列。
  3. 避免过多索引:每增加一个索引,写操作性能下降。
  4. 组合索引注意顺序:最常用的列放在前面;范围查询列放最后。
  5. 定期维护索引:重建或重组以消除碎片。
  6. 避免对小表建索引:全表扫描可能更快。
  7. 监控索引使用情况:通过 DMV 查看是否被使用。

四、创建索引

1. 使用对象资源管理器创建索引(图形界面操作)

步骤

  1. 展开数据库 → 表 → 目标表 → "索引"节点。
  2. 右键 → "新建索引" → 选择索引类型。
  3. 添加索引列,设置排序(升序/降序)。
  4. 可选:设置包含列、筛选条件、唯一性等。
  5. 点击"确定"完成。
    适用场景:适合初学者或临时调整,生产环境建议使用脚本。

2. 使用 Transact-SQL 语句创建索引

语法结构:
sql 复制代码
CREATE [UNIQUE] [CLUSTERED | NONCLUSTERED] INDEX index_name
ON table_name (column_name [ASC | DESC] [ ,...n ] )
[INCLUDE (column_name [ ,...n ] )]
[WHERE filter_predicate]
[WITH (
    PAD_INDEX = { ON | OFF },
    FILLFACTOR = fillfactor,
    SORT_IN_TEMPDB = { ON | OFF },
    IGNORE_DUP_KEY = { ON | OFF },
    STATISTICS_NORECOMPUTE = { ON | OFF },
    DROP_EXISTING = { ON | OFF },
    ONLINE = { ON | OFF },
    ALLOW_ROW_LOCKS = { ON | OFF },
    ALLOW_PAGE_LOCKS = { ON | OFF },
    MAXDOP = max_degree_of_parallelism
)]
[ON { partition_scheme_name | filegroup_name } ]

案例1:创建聚集索引
sql 复制代码
-- 在 Employees 表的 EmployeeID 列上创建聚集索引
-- 通常主键默认是聚集索引,但也可手动指定
CREATE CLUSTERED INDEX IX_Employees_EmployeeID
ON dbo.Employees (EmployeeID ASC);
GO

注释

  • CLUSTERED 表示聚集索引。
  • ASC 表示升序(默认),也可用 DESC 降序。
  • 一个表只能有一个聚集索引。

案例2:创建非聚集索引
sql 复制代码
-- 在 Employees 表的 LastName 列上创建非聚集索引,用于加速按姓氏查询
CREATE NONCLUSTERED INDEX IX_Employees_LastName
ON dbo.Employees (LastName ASC);
GO

注释

  • NONCLUSTERED 可省略(默认)。
  • 适用于高频查询但非主键的列。

案例3:创建唯一索引
sql 复制代码
-- 在 Employees 表的 Email 列上创建唯一非聚集索引,防止重复邮箱
CREATE UNIQUE NONCLUSTERED INDEX IX_Employees_Email
ON dbo.Employees (Email ASC)
WHERE Email IS NOT NULL; -- 可选:允许NULL值(多个NULL不违反唯一性)
GO

注释

  • UNIQUE 确保列值唯一。
  • WHERE Email IS NOT NULL 是筛选索引,只对非空值建索引。

案例4:创建包含列的索引(覆盖索引)
sql 复制代码
-- 查询常需要:LastName, FirstName, Department
-- 创建索引包含 LastName 作为键列,FirstName 和 Department 作为包含列
-- 避免回表查询(书签查找)
CREATE NONCLUSTERED INDEX IX_Employees_LastName_Includes
ON dbo.Employees (LastName ASC)
INCLUDE (FirstName, Department);
GO

注释

  • INCLUDE 子句将非键列包含在叶级别。
  • 适用于 SELECT 中的非 WHERE 列,提高覆盖查询效率。

案例5:创建复合索引(多列索引)
sql 复制代码
-- 经常按 Department 和 HireDate 范围查询
-- Department 选择性高放前面,HireDate 范围查询放后面
CREATE NONCLUSTERED INDEX IX_Employees_Dept_HireDate
ON dbo.Employees (Department ASC, HireDate DESC);
GO

注释

  • 复合索引顺序很重要:高选择性、等值查询列在前,范围查询列在后。
  • 查询 WHERE Department = 'IT' AND HireDate > '2020-01-01' 可高效使用此索引。

案例6:创建筛选索引
sql 复制代码
-- 只对在职员工(Status = 'Active')建索引,减少索引大小和维护开销
CREATE NONCLUSTERED INDEX IX_Employees_Active_LastName
ON dbo.Employees (LastName)
WHERE Status = 'Active';
GO

注释

  • WHERE 子句定义筛选条件。
  • 适用于数据子集查询频繁的场景。

案例7:创建索引并指定填充因子(FillFactor)
sql 复制代码
-- 设置填充因子为 80%,预留 20% 空间用于未来插入,减少页分裂
CREATE NONCLUSTERED INDEX IX_Employees_LastName_FillFactor
ON dbo.Employees (LastName)
WITH (FILLFACTOR = 80, ONLINE = ON); -- ONLINE=ON 允许在线操作(企业版)
GO

注释

  • FILLFACTOR = 80 表示每页填满80%,留20%空间。
  • ONLINE = ON 允许在创建索引时表仍可读写(仅企业版支持)。

五、管理和维护索引

1. 查看索引信息

方法1:使用系统视图 sys.indexes
sql 复制代码
-- 查看指定表的所有索引信息
SELECT 
    i.name AS IndexName,
    i.type_desc AS IndexType,
    c.name AS ColumnName,
    ic.is_included_column AS IsIncludedColumn,
    i.fill_factor AS FillFactor,
    i.is_unique AS IsUnique,
    i.has_filter AS HasFilter,
    i.filter_definition AS FilterDefinition
FROM sys.indexes i
INNER JOIN sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id
INNER JOIN sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
WHERE i.object_id = OBJECT_ID('dbo.Employees')
ORDER BY i.name, ic.key_ordinal;
GO
方法2:使用存储过程 sp_helpindex
sql 复制代码
-- 快速查看表索引结构
EXEC sp_helpindex 'dbo.Employees';
GO
方法3:查看索引使用统计(DMV)
sql 复制代码
-- 查看索引被使用的次数,识别无用索引
SELECT 
    OBJECT_NAME(i.object_id) AS TableName,
    i.name AS IndexName,
    i.type_desc AS IndexType,
    s.user_seeks,
    s.user_scans,
    s.user_lookups,
    s.user_updates,
    s.last_user_seek,
    s.last_user_scan
FROM sys.indexes i
LEFT JOIN sys.dm_db_index_usage_stats s ON i.object_id = s.object_id AND i.index_id = s.index_id AND s.database_id = DB_ID()
WHERE OBJECT_NAME(i.object_id) = 'Employees'
ORDER BY s.user_seeks + s.user_scans + s.user_lookups DESC;
GO

2. 重命名索引

sql 复制代码
-- 将索引 IX_Employees_LastName 重命名为 IX_Emp_LastName
EXEC sp_rename 
    @objname = 'dbo.Employees.IX_Employees_LastName', 
    @newname = 'IX_Emp_LastName', 
    @objtype = 'INDEX';
GO

注释

  • sp_rename 用于重命名对象。
  • 格式:'schema.table.index_name'
  • 重命名后需更新相关脚本或应用程序引用。

3. 删除索引

sql 复制代码
-- 删除名为 IX_Emp_LastName 的索引
DROP INDEX IX_Emp_LastName ON dbo.Employees;
GO

注释

  • 删除索引会释放空间,但可能影响查询性能。
  • 删除前建议检查索引使用情况。

六、综合性案例

综合案例1:为电商订单系统优化索引

sql 复制代码
-- 场景:Orders 表,包含百万级数据
-- 常见查询:
-- 1. 按 CustomerID 查询订单
-- 2. 按 OrderDate 范围查询
-- 3. 按 Status 查询(如 'Shipped')
-- 4. 查询时需返回 OrderID, OrderDate, TotalAmount, CustomerID

-- 步骤1:创建复合索引满足多条件查询
CREATE NONCLUSTERED INDEX IX_Orders_CustomerID_OrderDate
ON dbo.Orders (CustomerID ASC, OrderDate DESC)
INCLUDE (TotalAmount, Status); -- 覆盖常用查询列
GO

-- 步骤2:为状态查询创建筛选索引(只对未发货订单)
CREATE NONCLUSTERED INDEX IX_Orders_Status_Pending
ON dbo.Orders (OrderDate DESC)
INCLUDE (CustomerID, TotalAmount)
WHERE Status = 'Pending';
GO

-- 步骤3:查看索引是否创建成功
EXEC sp_helpindex 'dbo.Orders';
GO

-- 步骤4:模拟查询,验证索引使用
SET STATISTICS IO ON;
SELECT OrderID, OrderDate, TotalAmount, CustomerID
FROM dbo.Orders
WHERE CustomerID = 1001 AND OrderDate >= '2025-01-01'
ORDER BY OrderDate DESC;
-- 应使用 IX_Orders_CustomerID_OrderDate,逻辑读取大幅减少
GO
SET STATISTICS IO OFF;

综合案例2:索引维护脚本(重建高碎片索引)

sql 复制代码
-- 查找碎片率 > 30% 的索引,并重建
DECLARE @SchemaName NVARCHAR(128);
DECLARE @TableName NVARCHAR(128);
DECLARE @IndexName NVARCHAR(128);
DECLARE @SQL NVARCHAR(MAX);

DECLARE IndexCursor CURSOR FOR
SELECT 
    s.name AS SchemaName,
    t.name AS TableName,
    i.name AS IndexName
FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, 'LIMITED') ps
INNER JOIN sys.tables t ON ps.object_id = t.object_id
INNER JOIN sys.schemas s ON t.schema_id = s.schema_id
INNER JOIN sys.indexes i ON ps.object_id = i.object_id AND ps.index_id = i.index_id
WHERE ps.avg_fragmentation_in_percent > 30
    AND i.name IS NOT NULL
    AND ps.page_count > 1000; -- 忽略小索引

OPEN IndexCursor;
FETCH NEXT FROM IndexCursor INTO @SchemaName, @TableName, @IndexName;

WHILE @@FETCH_STATUS = 0
BEGIN
    SET @SQL = N'ALTER INDEX ' + QUOTENAME(@IndexName) + 
               N' ON ' + QUOTENAME(@SchemaName) + '.' + QUOTENAME(@TableName) + 
               N' REBUILD WITH (ONLINE = ON, FILLFACTOR = 90);';
    
    PRINT 'Rebuilding: ' + @SQL;
    EXEC sp_executesql @SQL;

    FETCH NEXT FROM IndexCursor INTO @SchemaName, @TableName, @IndexName;
END

CLOSE IndexCursor;
DEALLOCATE IndexCursor;
GO

注释

  • 使用 sys.dm_db_index_physical_stats 检测碎片。
  • REBUILD 重建索引,消除碎片。
  • ONLINE = ON 减少阻塞(企业版)。
  • 定期在维护窗口执行。

综合案例3:动态创建缺失索引建议(基于 DMV)

sql 复制代码
-- 查询 SQL Server 建议的缺失索引
SELECT 
    migs.avg_total_user_cost * (migs.avg_user_impact / 100.0) * (migs.user_seeks + migs.user_scans) AS improvement_measure,
    'CREATE NONCLUSTERED INDEX IX_' + 
    REPLACE(REPLACE(mid.statement, ']', ''), '[', '') + '_' + 
    REPLACE(REPLACE(ISNULL(mid.equality_columns,''), '], [', '_'), '[', '') + '_' +
    REPLACE(REPLACE(ISNULL(mid.inequality_columns,''), '], [', '_'), '[', '') + 
    ' ON ' + mid.statement + ' (' + 
    ISNULL(mid.equality_columns, '') +
    CASE WHEN mid.equality_columns IS NOT NULL AND mid.inequality_columns IS NOT NULL THEN ',' ELSE '' END +
    ISNULL(mid.inequality_columns, '') + ')' +
    ISNULL(' INCLUDE (' + mid.included_columns + ')', '') AS create_index_statement,
    migs.user_seeks,
    migs.user_scans,
    migs.last_user_seek,
    migs.avg_user_impact
FROM sys.dm_db_missing_index_groups mig
INNER JOIN sys.dm_db_missing_index_group_stats migs ON mig.index_group_handle = migs.group_handle
INNER JOIN sys.dm_db_missing_index_details mid ON mig.index_handle = mid.index_handle
WHERE migs.avg_total_user_cost * (migs.avg_user_impact / 100.0) * (migs.user_seeks + migs.user_scans) > 10
ORDER BY improvement_measure DESC;
GO

注释

  • 此脚本生成 SQL Server 建议的缺失索引语句。
  • 需结合业务分析,不可盲目创建。
  • improvement_measure 越高,潜在收益越大。

七、最佳实践总结

  1. 定期监控索引使用情况,删除无用索引。
  2. 避免过度索引,特别是写密集型表。
  3. 组合索引列顺序:等值查询在前,范围查询在后。
  4. 使用包含列减少书签查找。
  5. 大表使用筛选索引提高效率。
  6. 维护索引碎片:>30% 重建,10%~30% 重组。
  7. 测试索引效果 :使用 SET STATISTICS IO ON 对比逻辑读取。

✅ 以上内容涵盖 SQL Server 2019 索引的核心语法、管理操作及实战案例,可直接用于学习、开发与调优。

相关推荐
一 乐2 小时前
林业资源管理|基于java + vue林业资源管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·论文·毕设·林业资源管理系统
l1t2 小时前
DeepSeek总结的PostgreSQL 18 EXPLAIN 中新增的 Index Searches
数据库·postgresql
g***27992 小时前
使用 Canal 实时从 MySql 向其它库同步数据
数据库·mysql
u***35742 小时前
对基因列表中批量的基因进行GO和KEGG注释
开发语言·数据库·golang
小光学长2 小时前
基于ssm的校园约自习网站23i21xj4(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
java·数据库·spring
LSL666_2 小时前
1.3 云服务器安装和使用Redis
数据库·redis·缓存
LSL666_2 小时前
1.4 Redis服务端和客户端
数据库·redis·缓存
LSL666_2 小时前
1.7 Redis多数据库
数据库·redis·缓存
低代码布道师2 小时前
Next.js 16 全栈实战(三):数据库建模与动态菜单实现
开发语言·javascript·数据库