这个索引扫描中这个 Uniq1002 列的目的是什么?

Han*_*non 7 sql-server execution-plan database-internals sql-server-2012

采取以下再现:

USE tempdb;

IF OBJECT_ID(N'dbo.t', N'U') IS NOT NULL
DROP TABLE dbo.t
GO
CREATE TABLE dbo.t
(
    id int NOT NULL 
        PRIMARY KEY 
        NONCLUSTERED 
        IDENTITY(1,1)
    , col1 datetime NOT NULL
    , col2 varchar(800) NOT NULL
    , col3 tinyint NULL
    , col4 sysname NULL
);

INSERT INTO dbo.t (
      col1
    , col2
    , col3
    , col4
    ) 
SELECT TOP(100000) 
      CONVERT(datetime, 
         DATEADD(DAY, CONVERT(int, CRYPT_GEN_RANDOM(1)), '2000-01-01 00:00:00'))
    , replicate('A', 800)
    , sc2.bitpos
    , CONVERT(sysname, CHAR(65 + CRYPT_GEN_RANDOM(1) % 26) 
        + CHAR(65 + CRYPT_GEN_RANDOM(1) % 26) 
        + CHAR(65 + CRYPT_GEN_RANDOM(1) % 26))
FROM sys.syscolumns sc
    CROSS JOIN sys.syscolumns sc2;
Run Code Online (Sandbox Code Playgroud)

在这里,我将聚集索引添加到一组不唯一的列上,并且是典型的单列非聚集索引:

CREATE CLUSTERED INDEX t_cx 
ON dbo.t (col1, col2, col3);

CREATE INDEX t_c1 ON dbo.t(col4); 
Run Code Online (Sandbox Code Playgroud)

此查询强制 SQL Server 对聚集索引进行查找。请原谅使用索引提示,这是获得 repro 的最快方法:

SELECT id
    , col1
    , col2
    , col3
FROM dbo.t aad WITH (INDEX = t_c1)
WHERE col4 = N'JSB'
    AND col1 > N'2019-05-30 00:00:00';
Run Code Online (Sandbox Code Playgroud)

实际的查询计划显示在非聚簇索引扫描输出列表一个不存在的列:

在此处输入图片说明

从表面上看,这表示非唯一聚集索引中使用的 uniqifier。是这样吗?像这样命名的列是否总是聚集索引 uniqifier?

Pau*_*ite 9

从表面上看,这表示非唯一聚集索引中使用的 uniqifier。是这样吗?

是的。

这个索引扫描中这个 Uniq1002 列的目的是什么?

非聚集索引中的每一行都必须与基表中的一行正好相关联,以便书签查找(RID 或键)正常工作。此映射由“行定位器”提供。

对于堆表,行定位符是 RID。对于聚集行存储表,它是聚集键(必要时包括唯一符)。

要使计划中的Key Lookup起作用,它必须能够访问行定位器。这包括uniquifier,因此它必须由非聚集索引扫描发出。

所述唯一标志被存储在该行的可变长度部分,以便在需要的时候只占用空间(即,当重复的键实际存在)。

像这样命名的列总是聚集索引唯一符吗?

是的。uniquifier 列始终命名为UniqXXXX。与堆表关联的行定位器名为BmkXXXX。列存储表的行定位器名为ColStoreLocXXXX


观察唯一性

可以在包含功能性query_trace_column_values扩展事件的SQL Server 版本上直接观察 uniquifier 的值。

此未记录且不受支持的事件位于Debug通道中。它是在 SQL Server 2016 中引入的,并停止围绕 SQL Server 2017 的 CU11 工作。

例如:

CREATE TABLE #T (c1 integer NULL INDEX ic1 CLUSTERED, c2 integer NULL INDEX ic2 UNIQUE, c3 integer NULL);
GO
INSERT #T
    (c1, c2, c3)
VALUES 
    (100, 101, 0),
    (100, 102, 1),
    (100, 103, 2);
GO
DBCC TRACEON (2486);
SET STATISTICS XML ON;
SELECT T.* FROM #T AS T WITH (INDEX(ic2));
SET STATISTICS XML OFF;
DBCC TRACEOFF (2486);
GO
DROP TABLE #T;
Run Code Online (Sandbox Code Playgroud)

有没有计划:

计划

它在 SQL Server 2016 上产生如下事件输出:

事件输出