次の再現を見てください。
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;
ここでは、一意ではない列のセットにクラスター化インデックスを追加し、典型的な単一列の非クラスター化インデックスを追加しています。
CREATE CLUSTERED INDEX t_cx
ON dbo.t (col1, col2, col3);
CREATE INDEX t_c1 ON dbo.t(col4);
このクエリは、SQL Serverがクラスター化インデックスを検索するように強制します。インデックスのヒントの使用を許してください、それは再現を取得する最も速い方法でした:
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';
実際のクエリプラン は、非クラスター化インデックススキャンの出力リストに存在しない列を示します。
表面上は、これは非一意のクラスター化インデックスで使用される一意修飾子を表しています。それは事実ですか?そのような名前の列はalwaysクラスター化インデックスuniqifierですか?
表面的には、これは非一意のクラスター化インデックスで使用される一意修飾子を表しています。それは事実ですか?
はい。
このインデックススキャンにおけるこのUniq1002列の目的は何ですか?
非クラスター化インデックスの各行は、ベーステーブルの1行に関連付けられている必要があるため、ブックマークルックアップ(RIDまたはキー)は正しく動作します。このマッピングは、「行ロケーター」によって提供されます。
ヒープテーブルの場合、行ロケータはRIDです。クラスター化された行ストアテーブルの場合、それはクラスタリングキーです(必要に応じてuniquifierを含めます)。
計画のKey Lookupが機能するには、行ロケーターにアクセスできる必要があります。これにはuniquifierが含まれるため、非クラスター化インデックススキャンによって発行される必要があります。
uniquifierは行の可変長部分に格納されるため、必要な場合(つまり、重複するキーが実際に存在する場合)にのみ領域を占有します。
そのような名前の列は常にクラスター化インデックスの一意化ですか?
はい。一意識別子列は常にUniqXXXX
という名前になります。ヒープテーブルに関連付けられた行ロケータはBmkXXXX
という名前です。列ストアテーブルの行ロケーターの名前はColStoreLocXXXX
です。
機能 query_trace_column_values
拡張イベント を含むSQL Serverバージョンの一意化子の値を直接監視することが可能です。
この文書化されていない、サポートされていないイベントは、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;
計画があります:
SQL Server 2016では、次のようなイベント出力が生成されます。
SQL Serverが非一意のクラスター化インデックスを作成するために、非表示の「列」がクラスター化インデックスの物理構造に追加されます。その非表示の列はuniqifierと呼ばれ、その名前が示すように、クラスター化インデックスのすべての行が一意であることを確認するメカニズムを提供します。
クエリプランにその列が表示されている場合、クラスター化キー列が一意として定義されていないことを示す優れた指標です。列の組み合わせが一意ではないことがわかっているためと考えられます。また、テーブルの設計者がUNIQUE
修飾子をCREATE CLUSTERED INDEX
ステートメントに追加するのを忘れている可能性もあります。
実際、一意のクラスター化インデックスを使用して上記の再現を再作成すると、Uniq1002
列はクエリプランに表示されなくなります。
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 int 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)
, CONVERT(int, CRYPT_GEN_RANDOM(4))
, 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;
一意のクラスター化インデックスは次のとおりです。
CREATE UNIQUE CLUSTERED INDEX t_cx
ON dbo.t (col1, col2, col3);
CREATE INDEX t_c1 ON dbo.t(col4);
そしてクエリ:
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';
計画では、非クラスター化インデックススキャンの出力列についてこれを示しています。
一意でないクラスター化インデックスが作成されると、一意名が自動的に追加されます。インデックスのプロパティを調べたり、インデックスを「スクリプト化」したりして「見る」ことができない場合でも、uniqifierはすべての非クラスター化インデックスにも追加されます。
Uniqifierは、テーブルに挿入された各行の背後で自動的にインクリメントされる整数を含む4バイトの列です。挿入された最初の行は、一意修飾子を必要としません。最初の行の後に追加された行にのみ、uniqifierが存在します。