MS SQL巡检系列——检查外键字段是否缺少索引
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MS SQL巡检系列——检查外键字段是否缺少索引相关的知识,希望对你有一定的参考价值。
前言感想:一时兴起,突然想写一个关于MS SQL的巡检系列方面的文章,因为我觉得这方面的知识分享是有价值,也是非常有意义的。一方面,很多经验不足的人,对于巡检有点茫然,不知道要从哪些方面巡检,另外一方面,网上关于MS SQL巡检方面的资料好像也不是特别多。写这个系列只是一个分享,自己的初衷是一个知识梳理、总结提炼过程,有些知识和脚本也不是原创,文章很多地方融入了自己的一些想法和见解的,不足和肤浅之处肯定也非常多,抛砖引玉,也希望大家提意见和建议、补充,指正其中的不足之处。Stay Hungry Stay Foolish!
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开销就比较大。
我们对外键约束字段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;
你会发现执行计划从原来的聚集索引扫描(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;
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;
以上是关于MS SQL巡检系列——检查外键字段是否缺少索引的主要内容,如果未能解决你的问题,请参考以下文章
oracle(sql)基础篇系列——PLSQL游标存储过程触发器