给定一些带有主键的表,例如:
CREATE TABLE Customers (
CustomerID int NOT NULL PRIMARY KEY,
FirstName nvarchar(50),
LastName nvarchar(50),
Address nvarchar(200),
Email nvarchar(260)
--...
)
Run Code Online (Sandbox Code Playgroud)
我们有一个唯一的主键CustomerID
。
传统上,我可能需要一些额外的覆盖索引;例如通过CustomerID
或快速找到用户Email
:
CREATE INDEX IX_Customers_CustomerIDEmail ON Customers
(
CustomerID,
Email
)
Run Code Online (Sandbox Code Playgroud)
这些是我几十年来创建的索引类型。
索引本身的存在是为了避免表扫描;它是一个覆盖索引,以帮助提高性能(该索引不是强制执行唯一性的约束)。
今天我想起了一点信息——SQL Server 可以使用以下事实:
以帮助它优化其查询执行。事实上,来自SQL Server 索引设计指南:
如果数据是唯一的并且您希望强制执行唯一性,则在相同的列组合上创建唯一索引而不是非唯一索引可为查询优化器提供附加信息,从而可以生成更高效的执行计划。在这种情况下,建议创建唯一索引(最好通过创建 UNIQUE 约束)。
鉴于我的多列索引包含主键,这个复合索引实际上是唯一的。这不是我特别需要 SQL Server 在每次插入或更新期间强制执行的约束;但事实是这个非聚集索引是唯一的。
将此事实上的唯一索引标记为实际唯一索引是否有任何优势?
在客户上创建唯一索引 IX_Customers_CustomerIDEmail ( 顾客ID, 电子邮件 )
在我看来,SQL Server可能足够聪明,可以意识到我的索引已经是唯一的,因为它包含主键。
当复合索引包含主键时,我找不到 Microsoft 提供的有关如何操作的任何指导。
唯一索引的好处包括:
- 确保定义列的数据完整性。
- 提供了对查询优化器有用的附加信息。
如果复合索引已经包含主键,我是否应该将其标记为唯一?或者 SQL Server 可以自己解决这个问题吗?
Pau*_*ite 12
如果复合索引已经包含主键,我是否应该将其标记为唯一?
可能不是。无论如何,优化器通常可以使用有关包含的键列的唯一性的信息,因此没有真正的优势。
在修改索引键的更新计划上标记唯一索引还有一个重要的后果是:
CREATE TABLE dbo.Customers
(
CustomerID int NOT NULL PRIMARY KEY,
FirstName nvarchar(50),
LastName nvarchar(50),
[Address] nvarchar(200),
Email nvarchar(260)
);
CREATE NONCLUSTERED INDEX
IX_Customers_CustomerIDEmail
ON dbo.Customers
(
CustomerID,
Email
);
-- Pretend we have some rows
UPDATE STATISTICS dbo.Customers
WITH ROWCOUNT = 100000, PAGECOUNT = 20000;
Run Code Online (Sandbox Code Playgroud)
UPDATE dbo.Customers
SET Email = N'New', [Address] = 'New Address'
WHERE Email = N'Old'
OPTION (QUERYTRACEON 8790); -- Per-index update plan
Run Code Online (Sandbox Code Playgroud)
执行计划:
优化器通常会在每行(“窄”计划)或每索引(“宽”计划)更新非聚集索引之间做出基于成本的决定。默认策略(内存中 OLTP 表除外)是一个广泛的计划。
窄计划(其中非聚集索引与堆/聚集索引同时维护)是针对小更新的性能优化。这种优化并不是在所有情况下都实现的 - 使用某些功能(如索引视图)意味着相关的索引将在一个广泛的计划中维护。
更多信息:优化更改数据的 T-SQL 查询
在这种情况下,我使用了未记录的跟踪标志 8790 来强制执行一个广泛的更新计划:因此该计划显示聚集索引和非聚集索引是分开维护的。
Split 将每次更新变成单独的删除和插入对;过滤器过滤掉不会导致索引更改的任何行。
更多信息:(非更新更新)由 SQL Server QO 团队提供。
-- Same index, but unique
CREATE UNIQUE INDEX IX_Customers_CustomerIDEmail ON Customers
(
CustomerID,
Email
)
WITH (DROP_EXISTING = ON);
UPDATE dbo.Customers
SET Email = N'New', [Address] = 'New Address'
WHERE Email = N'Old'
OPTION (QUERYTRACEON 8790); -- Per-index update plan
Run Code Online (Sandbox Code Playgroud)
执行计划:
当索引被标记为唯一时,请注意额外的 Sort 和 Collapse 操作符。
更新唯一索引的键时需要这种拆分-排序-折叠模式,以防止中间唯一键违规。
更多信息:维护唯一索引作者:Craig Freedman
特别是 Sort 可能是一个问题。这不仅是不必要的额外成本,而且如果估计不准确,它可能会溢出到磁盘上。
另一个需要考虑的因素是非聚集索引结构在索引的每个级别总是唯一的,即使UNIQUE
没有指定。聚簇键——如果聚簇索引没有标记为唯一,可能还有一个唯一符——被添加到所有级别的非唯一非聚簇索引中。
因此,以下索引定义:
CREATE INDEX IX_Customers_CustomerIDEmail ON Customers
(
Email
)
WITH (DROP_EXISTING = ON);
Run Code Online (Sandbox Code Playgroud)
...实际上包含所有级别的键(电子邮件、客户 ID)。因此,它在两列上都是“可搜索的”:
SELECT *
FROM dbo.Customers AS C WITH (INDEX(IX_Customers_CustomerIDEmail))
WHERE C.Email = N'Email'
AND C.CustomerID = 1;
Run Code Online (Sandbox Code Playgroud)
更多信息:Kalen Delaney关于非聚集索引键的更多信息
SQL 已经知道它是唯一的(如果它包含 PK,它就不能变得更唯一),无论您是否明确告诉它。
非唯一索引和唯一索引之间的最大区别在于,非唯一索引需要在索引的更高级别上的聚集索引键(如果 CIX 未声明为唯一,则使用唯一值),而不仅仅是在叶等级。
在您的情况下,您已经在键中拥有 CIX,这意味着它将已经在索引的每个级别。
但是您可以创建一个具有单独 PK(唯一)和 CIX(无关紧要)的表。然后创建一个非唯一索引,该索引在其键中包含 PK。将一些行放入表中,包括 CIX 列的一些易于查找的 varchar 值。放入足够多的行以产生多个级别的索引。然后你可以用DBCC IND在你的NCIX中找到页面,然后DBCC PAGE打开一些查看数据,看看CIX键值是否在更高级别。
归档时间: |
|
查看次数: |
2120 次 |
最近记录: |