MS SQL巡检系列——检查外键字段是否缺少索引

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MS SQL巡检系列——检查外键字段是否缺少索引相关的知识,希望对你有一定的参考价值。

前言感想一时兴起,突然想写一个关于MS SQL的巡检系列方面的文章,因为我觉得这方面的知识分享是有价值,也是非常有意义的。一方面,很多经验不足的人,对于巡检有点茫然,不知道要从哪些方面巡检,另外一方面,网上关于MS SQL巡检方面的资料好像也不是特别多。写这个系列只是一个分享,自己的初衷是一个知识梳理、总结提炼过程,有些知识和脚本也不是原创,文章很多地方融入了自己的一些想法和见解的,不足和肤浅之处肯定也非常多,抛砖引玉,也希望大家提意见和建议、补充,指正其中的不足之处。Stay Hungry Stay Foolish!

 

MS SQL巡检系列——检查重复索引

MS SQL巡检系列——检查外键字段是否缺少索引

MS SQL巡检系列——检查数据库上一次DBCC CHECKDB的时间

 

对于绝大部分情况,外键字段都有必要建立对应的索引(注意,外键约束并不会自动建立索引),关于外键字段为什么要建立索引?下面从几个简单的例子来分析一下。我们先准备测试环境数据。

CREATE TABLE PRIMARY_TB
(
    PRODUCT_CD        VARCHAR(12)      ,
    PRODUCT_DATE    DATE             ,
    PRODUCT_DESC    VARCHAR(120)     ,
    CONSTRAINT PK_PRIMARY_TB  PRIMARY KEY CLUSTERED (PRODUCT_CD)
);
 
 
SET NOCOUNT ON;
GO
DECLARE @Index INT=1;
 
 
BEGIN TRAN
WHILE @Index <= 3000
BEGIN
 
    INSERT INTO dbo.PRIMARY_TB
    SELECT \'Prd\' + CAST(@Index AS VARCHAR(4)), GETDATE() - CEILING(RAND()*200), \'production description\' + CAST(@Index AS VARCHAR(4));
 
    SET @Index +=1;
END
 
COMMIT;
 
 
 
CREATE TABLE FK_TB
(
    FK_ID            INT IDENTITY(1,1),
    SALES_REGION    VARCHAR(32),
    SALES_CITY        VARCHAR(32),
    PRODUCT_CD        VARCHAR(12),
    SALIES_SUM        INT,
    CONSTRAINT PK_FK_TB PRIMARY KEY CLUSTERED (FK_ID)
)
GO
 
ALTER TABLE [dbo].[FK_TB]  WITH CHECK ADD  CONSTRAINT [FK_PRIMARY_TB_PRODUCT_CD] FOREIGN KEY([PRODUCT_CD])
REFERENCES [dbo].[PRIMARY_TB] ([PRODUCT_CD]) ON  DELETE CASCADE;
GO
 
 
SET NOCOUNT ON;
GO
DECLARE @Index INT=1;
 
BEGIN TRAN
WHILE @Index <=1000000
BEGIN
    INSERT INTO FK_TB
    SELECT \'REGION\'+CAST(CEILING(RAND()*20) AS VARCHAR(2)), CAST(CEILING(RAND()*300) AS VARCHAR(3)),\'Prd\'+ CAST(CEILING(RAND()*3000) AS VARCHAR(8)),CEILING(RAND()*100000);
 
    SET @Index +=1;
 
END
 
COMMIT;
 
 
UPDATE STATISTICS dbo.PRIMARY_TB WITH FULLSCAN;
UPDATE STATISTICS dbo.FK_TB WITH FULLSCAN;
GO

 

1: 外键字段建立索引,在主表与子表JOIN操作时能提高性能,减少IO操作。

DBCC DROPCLEANBUFFERS;
GO
DBCC FREEPROCCACHE;
GO
SET STATISTICS IO ON;
SET STATISTICS TIME ON;
 
SELECT  p.PRODUCT_CD ,
        p.PRODUCT_DATE ,
        f.SALES_REGION ,
        f.SALES_CITY ,
        f.SALIES_SUM
FROM    dbo.PRIMARY_TB p
        INNER JOIN dbo.FK_TB f ON p.PRODUCT_CD = f.PRODUCT_CD
WHERE p.PRODUCT_CD =\'Prd131\';
SET STATISTICS IO OFF;
SET STATISTICS TIME OFF;

 

如下截图所示,如果外键字段缺少索引,这两个表关联查询时,子表就会走扫描(此处测试是聚集索引扫描),如果子表非常大(例如此处案例所示),IO开销就比较大。

clipboard

clipboard

 

我们对外键约束字段PRODUCT_CD建立下面非聚集索引IDX_FK_TB,然后对比两者的执行计划和IO开销

CREATE INDEX IDX_FK_TB ON dbo.FK_TB(PRODUCT_CD);
 
DBCC DROPCLEANBUFFERS;
GO
DBCC FREEPROCCACHE;
GO
 
SET STATISTICS IO ON;
SET STATISTICS TIME ON;
SELECT  p.PRODUCT_CD ,
        p.PRODUCT_DATE ,
        f.SALES_REGION ,
        f.SALES_CITY ,
        f.SALIES_SUM
FROM    dbo.PRIMARY_TB p
        INNER JOIN dbo.FK_TB f ON p.PRODUCT_CD = f.PRODUCT_CD
WHERE p.PRODUCT_CD =\'Prd131\'
 
SET STATISTICS IO OFF;
SET STATISTICS TIME OFF;

clipboard

clipboard

 

你会发现执行计划从原来的聚集索引扫描(Clustered Index Scan)变成了索引查找(Index Seek),IO的减少也是非常明显的。因为这里仅仅是测试数据,复杂的生产环境,性能的提升有可能比这更加明显。

 

2:如果外键约束为CASCADE(on update/delete)时,则当修改被引用行(referenced row)时,所有引用行(referencing rows )也必须修改(更新或级联删除)。外键列上的索引能减小锁的粒度和范围,从而提高效率和性能。如下所示:

 

我们先看看缺少索引的情况。

DROP INDEX IDX_FK_TB ON dbo.FK_TB;
 
 
 
DBCC DROPCLEANBUFFERS;
GO
DBCC FREEPROCCACHE;
GO
 
SET STATISTICS IO ON;
SET STATISTICS TIME ON;
 
DELETE FROM dbo.PRIMARY_TB WHERE PRODUCT_CD IN (\'Prd132\',\'Prd133\')
GO
SET STATISTICS IO OFF;
SET STATISTICS TIME OFF;

clipboard

clipboard

CREATE INDEX IDX_FK_TB ON dbo.FK_TB(PRODUCT_CD);
 
GO
 
DBCC DROPCLEANBUFFERS;
 
GO
 
DBCC FREEPROCCACHE;
 
GO
 
SET STATISTICS IO ON;
 
SET STATISTICS TIME ON;
 
DELETE FROM dbo.PRIMARY_TB WHERE PRODUCT_CD IN (\'Prd134\',\'Prd135\')
 
GO
 
SET STATISTICS IO OFF;
 
SET STATISTICS TIME OFF;

clipboard

clipboard

 

以上是关于MS SQL巡检系列——检查外键字段是否缺少索引的主要内容,如果未能解决你的问题,请参考以下文章

oracle(sql)基础篇系列——PLSQL游标存储过程触发器

oracle(sql)基础篇系列——数字字典索引序列三范式

oracle(sql)基础篇系列——多表连接查询子查询视图

SQL Server虚拟化系列——构建理想的基于VMware的SQL Server虚拟机

mybatis基础系列——mybatis入门

SharePoint Server 2016 部署安装—— 安装SQL Server 2016(上)