我在数据库中发现一个表,同一列上有两个单独的索引。列类型为int
,并且该列上有一个聚集主键。除此之外,同一列上还有唯一的非聚集索引。索引具有相同的选项(排序方向和其他),并且不包含任何包含的列。
该索引被其他一些表中的外键约束使用,因此我无法在不重新创建外键约束的情况下删除它。
这可能有任何合理的理由吗?
最佳答案
也许是为了效率。非聚集索引通常小于聚集索引,因为叶级别的聚集索引包含所有(非 LOB)字段。所以也许它更喜欢使用非聚集索引来强制执行外键约束。
更新:我使用 AdventureWorks 数据库做了一些进一步的测试,证实了这个理论。见下文。
我可以使用两个表 T1 和 T2 重现该问题。 T1 是父级,T2 到 T1 之间存在外键关系。
当 T1 具有聚集主键约束和非聚集唯一索引 Ix-T1 时,我可以更改表并删除聚集主键约束,但无法像您所发现的那样删除 Ix-T1。
如果我使用非聚集主键约束和聚集唯一索引 Ix_T1 创建 T1,则情况相反:我可以删除 Ix-T1,但无法删除主键约束。
CREATE TABLE T1
(
id int NOT NULL CONSTRAINT PK_T1 PRIMARY KEY CLUSTERED
);
CREATE UNIQUE NONCLUSTERED INDEX Ix_T1
ON T1(id);
CREATE TABLE T2
(
id2 int NOT NULL PRIMARY KEY CLUSTERED,
id1 int NOT NULL FOREIGN KEY REFERENCES dbo.T1(id)
);
INSERT INTO T1 (id)
VALUES (1), (2), (3), (4);
INSERT INTO T2 (id2, id1)
VALUES (11, 1), (12, 2), (13, 3);
尝试删除非聚集索引。这失败了。
DROP INDEX Ix_T1
ON dbo.T1;
但是我可以删除聚集主键约束。
ALTER TABLE dbo.T1
DROP CONSTRAINT PK_T1;
使用具有非聚集主键和聚集唯一索引的 T1 重复测试。
CREATE TABLE T1
(
id int NOT NULL CONSTRAINT PK_T1 PRIMARY KEY NONCLUSTERED
);
CREATE UNIQUE CLUSTERED INDEX Ix_T1
ON T1(id);
这一次,我无法删除主键约束。
ALTER TABLE dbo.T1
DROP CONSTRAINT PK_T1;
但是我可以删除聚集索引。
DROP INDEX Ix_T1
ON dbo.T1;
因此,如果我的理论是正确的,那么如果删除非聚集索引,性能可能会受到影响。您可能想做一些调查和测试。
数据库模式是否有任何文档解释索引存在的原因?或者你可以问问设计数据库的人吗?
我使用 AdventureWorks2014 做了一些进一步的测试,这证实了我的理论。
USE AdventureWorks2014;
GO
CREATE SCHEMA test;
GO
-- Create two test tables
SELECT *
INTO test.SalesOrderHeader
FROM Sales.SalesOrderHeader;
SELECT *
INTO test.SalesOrderDetail
FROM Sales.SalesOrderDetail;
-- Test 1 - Clustered primary key and nonclustered index
ALTER TABLE test.SalesOrderHeader
ADD CONSTRAINT PK_Test_SalesOrderHeader PRIMARY KEY CLUSTERED (SalesOrderID);
CREATE UNIQUE NONCLUSTERED INDEX Ix_Test_SalesOrderHeader
ON test.SalesOrderHeader(SalesOrderID);
-- Test 2 - Nonclustered primary key and clustered index
CREATE UNIQUE CLUSTERED INDEX Ix_Test_SalesOrderHeader
ON test.SalesOrderHeader(SalesOrderID);
ALTER TABLE test.SalesOrderHeader
ADD CONSTRAINT PK_Test_SalesOrderHeader PRIMARY KEY NONCLUSTERED (SalesOrderID);
-- Test 3 - Clustered primary key only
ALTER TABLE test.SalesOrderHeader
ADD CONSTRAINT PK_Test_SalesOrderHeader PRIMARY KEY CLUSTERED (SalesOrderID);
-- Same for all tests
ALTER TABLE test.SalesOrderDetail
ADD CONSTRAINT PK_Test_SalesOrderDetail PRIMARY KEY CLUSTERED (SalesOrderDetailID);
ALTER TABLE test.SalesOrderDetail
ADD CONSTRAINT FK_Test_SalesOrderDetail_SalesOrderHeader FOREIGN KEY (SalesOrderID) REFERENCES test.SalesOrderHeader(SalesOrderID);
-- Update 100 records in SalesOrderDetail
UPDATE test.SalesOrderDetail
SET SalesOrderID = SalesOrderID + 1
WHERE SalesOrderDetailID BETWEEN 57800 AND 57899;
测试 1 的实际执行计划。
测试 2 的实际执行计划。 Index Seek 运算符的估计子树成本几乎与测试 1 相同。
测试 3 的实际执行计划。索引查找的估计子树成本超过测试 1 或测试 2 的两倍。
这是一个测量索引大小的查询。 (测试1配置。)您可以清楚地看到聚集索引要大得多。
-- Measure sizes of indexes
SELECT I.object_id, I.name, I.index_id, I.[type], I.[type_desc], SUM(s.used_page_count) * 8 AS 'IndexSizeKB'
FROM sys.indexes AS I
INNER JOIN sys.dm_db_partition_stats AS S
ON S.[object_id] = I.[object_id] AND S.index_id = I.index_id
WHERE I.[object_id] = OBJECT_ID('test.SalesOrderHeader')
GROUP BY I.object_id, I.name, I.index_id, I.[type], I.[type_desc];
以下是一些解释聚集索引和非聚集索引的引用资料。
TechNet > 表和索引数据结构架构:https://technet.microsoft.com/en-us/library/ms180978(v=sql.105).aspx
培训套件 70-462 管理 Microsoft SQL Server 2012 数据库 > 第 10 章:索引和并发 > 第 1 课:实现和维护索引
Microsoft SQL Server 2012 内部结构,作者:Kalen Delaney > 第 7 章:索引:内部结构和管理
关于sql-server - 聚集索引和非聚集索引之间的性能差异,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37136640/