假设我有一张表,其中有许多我不关心的列,但有两列我很关心:Primary
和Secondary
。 上有一个聚集主键Primary
。
CREATE TABLE [dbo].[OnlyTwoGoodColumnsButManyBad]
(
[Primary] INT PRIMARY KEY CLUSTERED,
[Secondary] NVARCHAR(500) NOT NULL,
[Evil1] NVARCHAR(MAX),
[Evil2] NVARCHAR(MAX),
[Evil3] NVARCHAR(MAX),
[Evil4] NVARCHAR(MAX),
[Evil5] NVARCHAR(MAX)
);
因为我不关心任何其他列,所以我决定我想要这个索引:
CREATE NONCLUSTERED INDEX [IX_Primary_Secondary]
ON [dbo].[OnlyTwoGoodColumnsButManyBad]
(
[Primary],
[Secondary]
);
鉴于此索引的键列表包含主键,因此毫无疑问该键列表是唯一的。因此,我很想这样做UNIQUE
。
CREATE UNIQUE NONCLUSTERED INDEX [UIX_Primary_Secondary]
ON [dbo].[OnlyTwoGoodColumnsButManyBad]
(
[Primary],
[Secondary]
);
但这有什么好处吗?如果情况完全相同,但以 作为Secondary
前导索引键,后面跟着 ,情况又如何呢Primary
?
独特性和性能
支持声明唯一性的论据
通常,向优化器提供更多信息比提供更少信息更好。如果您知道索引是唯一的,最好这样声明它。这可以帮助优化器,并向查看架构定义的实际人员提供有关您的数据的清晰度。
这会直接影响性能。请参阅 Paul White 的文章《强制唯一性以提高性能》中的示例。
反对声明唯一性的论点
至于这个问题的具体细节,我尝试将您的非聚集索引场景改编为 Paul 文章中的演示,并且似乎无论非聚集索引是否声明为唯一,都会使用单例查找。我已在本答案的末尾附上了修改后的演示代码。
基于此,优化器似乎能够通过包含来自聚簇索引的 UNIQUE 键来推断唯一性。这个概念在以下问答中得到了更彻底的证实和充实:如果复合索引包含主键,我是否应该将其标记为唯一?
在某些情况下,UNIQUE 索引可能会导致性能开销,因此遗憾的是,这里没有灵丹妙药。请参阅以下文章以了解相关阅读内容:
索引键排序
关于列排序,这实际上取决于针对此表执行的查询类型。这或多或少是这里涉及的有关索引的“一般”指导。您通常希望首先选择“最具选择性”的列。
如果查询通常将“Primary”的值作为过滤器(或连接条件),那么这是一个理想的候选 - 您可以直接查找正确的行。如果查询通常只提供“Secondary”的值,那么 Primary 不是一个好的前导索引键 - 您必须扫描表中的每一行。在这种情况下,Secondary 是引导索引的更好选择。
演示代码
请参阅下面我使用的代码,这也是根据 Paul 的文章改编的(可能不正确,因为我只是一个低级的 Web 开发人员):
将索引声明为唯一的还有另一个好处。
每个
non unique index
总是比相似的大unique index
。这是因为在非唯一非聚集索引的情况下,rowID(RID o 聚集索引键)也存储在非叶索引级别。非唯一的非聚集索引(您的情况)始终存在
clustered index key
于每个索引级别上,而类似的唯一索引在其非叶级别上不需要它,因此您的聚集索引键越大,non unique
非聚集索引的大小就越大。我找不到官方文档,但这里有一个解释:为什么非唯一非聚集索引的根和中间级别还要存储行 ID?
好的,这是我的复制品:
我刚刚从我的工作表 dbo.Nums 中创建了一个表,其中包含整数 1..1000000,我将唯一的列 n 放在新表的第 n 列中,然后创建另一列
replicate('0', 200) + cast(n as varchar(10)) as n1
,这里复制仅用于获得足够大的列,不仅可以获得根 + 叶,还可以获得 1 个中间级别。此列上的索引不是唯一的,但内容根据定义是唯一的。
然后我使用 DBCC IND 获取索引页:
我选取代表中级和根级别的 2 页并进行检查:
中级
根级别
它们都以聚集索引列 na 作为键的一部分。但列 n1 中的值是唯一的。