[英]Clustered index key should be part of the non-clustered index key, but how can I see it?
在 SQL Server 中,当表上定义了聚簇索引时,聚簇索引键将作为“隐藏”键隐式添加到任何非聚簇索引中。 但是为什么这个“隐藏”的列没有出现在元数据查询中呢?
例如,如果我定义以下简单表:
USE [tempdb];
DROP TABLE IF EXISTS abc;
CREATE TABLE abc (col1 INT NOT NULL, col2 INT NOT NULL, col3 INT);
ALTER TABLE [dbo].[abc] ADD CONSTRAINT PK_abc PRIMARY KEY CLUSTERED ([col1]);
CREATE NONCLUSTERED INDEX IX_2 ON [dbo].[abc] ([col3])
然后运行一个查询来查看索引信息:
SELECT object_schema_name(t.object_ID)+'.'+t.name AS The_Table,
i.[name] AS The_Index,
i.[type_desc],
index_column_id,
col_name(Ic.Object_Id, Ic.Column_Id) AS The_Column --the column
FROM sys.tables t
INNER JOIN sys.indexes i
ON t.object_ID=i.object_ID
INNER JOIN sys.Index_columns ic
ON i.Object_ID=ic.Object_ID
AND i.index_ID=ic.index_ID
WHERE i.object_id = OBJECT_ID('dbo.abc') --<== filter by table if necessary
ORDER BY t.name,i.index_id, index_column_id;
我明白了:
The_Table The_Index type_desc index_column_id The_Column
------------ ------------ ---------------- --------------- -------------
dbo.abc PK_abc CLUSTERED 1 col1
dbo.abc IX_2 NONCLUSTERED 1 col3
因此很容易看出非聚集索引不包含“col1”,我希望将其包含在内,因为它是聚集索引键。
有没有办法看到这个列实际上是非聚集索引的一部分?
查看它的一种方法是从一个简单的表中执行 SELECT 并查看它使用了哪些索引。
在下面的设置中,我有
(注意 - 我使用数字表来填充具有 10,000 行的临时表)。
CREATE TABLE #Test (C1 int PRIMARY KEY NONCLUSTERED, C2 int, C3 int, C4 nvarchar(50));
CREATE CLUSTERED INDEX #CX_Test ON #Test (C2);
CREATE NONCLUSTERED INDEX #IX_Test ON #Test (C3);
-- Populate test with 10,000 rows
INSERT INTO #Test (C1, C2, C3, C4)
SELECT n, n, n, N'Text here to fill up space so more reads on data'
FROM dbo.Numbers
WHERE n <= 10000;
UPDATE STATISTICS #Test;
现在,运行SET STATISTICS TIME, IO ON;
并打开“包括实际执行计划”,然后执行以下操作
SELECT C3, C2
FROM #Test;
SELECT C3, C1
FROM #Test;
第一个 (SELECTING C3, C2) 仅对非聚集索引进行索引扫描 - #IX_Test - 显示聚集列也包含在非聚集索引中。
另一方面,第二个 (SELECTING C3, C1) 执行聚簇索引扫描以获取 C1 字段中所需的信息——尽管在本例中,它是主键。 但是,因为它不是非聚集索引的一部分,它必须从更笨重的聚集索引中获取信息(相应地,逻辑读取次数比第一个索引高 8 倍)。
最后,你可以做另一种测试
SELECT TOP 10 C3, C2
FROM #Test
ORDER BY C3;
SELECT TOP 10 C3, C1
FROM #Test
ORDER BY C3;
这些和上面类似,但是因为它只是用 ORDER BY C3 取 TOP 10,所以使用非聚集索引是有用的。
在第一个中,它只需要读取非聚集索引,因为 C2 也包含在非聚集索引中。
在第二个中,它需要进行键查找以获取 C1 数据,因为它不包含在非聚集索引中。
最后,如果我删除聚集索引(所以现在表没有聚集索引 - 它只是一个堆)并再次运行最后的语句
DROP INDEX #CX_Test ON #Test;
SELECT TOP 10 C3, C2
FROM #Test
ORDER BY C3;
SELECT TOP 10 C3, C1
FROM #Test
ORDER BY C3;
这些计划彼此相同——从非聚集索引读取,然后执行 RID 查找(例如,使用 Row_ID 而不是聚集索引)以获取其他字段。
删除聚簇索引有效地改变了非聚簇索引的内容,但并没有真正改变它的定义。
编辑:这是一个有点违反直觉的。 使用最初的索引(在删除聚簇索引之前),运行以下命令。
SELECT C2
FROM #Test;
在这种情况下,它会从非聚集索引中进行读取,即使在索引 (C3) 中实际指定的字段实际上并不在 SELECT 语句中。 SQL Server 简单地问自己“我应该从聚簇索引(它有很多额外的读取)或较小的非聚簇索引中获取数据吗?” - 并且显然决定非集群是要走的路。
更新 - 数据方法
在评论中,OP 指定他们想要一种通过元数据的方法。
我一直无法获得元数据方法,但我找到了一种可以使用 DBCC 直接查看实际数据的方法,这表明包含聚簇索引。
这是基于https://www.mssqltips.com/sqlservertip/1578/using-dbcc-page-to-examine-sql-server-table-and-index-data/ - 但请注意,您似乎不再需要设置跟踪标志 3604。
为此,我将在数据库Testdb
中创建一个新表TestTab
,其设置与上面类似(非聚集主键、单独的聚集索引、一个用于非聚集索引的字段和另一个 misc 字段)。
CREATE TABLE TestTab (
TT_ID int PRIMARY KEY NONCLUSTERED,
TT_CX int,
TT_IX_field nvarchar(5),
TT_Other_field nvarchar(50)
);
CREATE CLUSTERED INDEX CX_TestTab ON TestTab (TT_CX);
CREATE NONCLUSTERED INDEX IX_TestTab ON TestTab (TT_IX_field);
INSERT INTO TestTab (TT_ID, TT_CX, TT_IX_Field, TT_Other_field) VALUES
(1, 15, 'DKTPE', 'Random text goes here'),
(2, 10, 'GEWPX', 'More random text');
现在,第一个命令是获取相关页面信息
DBCC IND('Testdb', TestTab, -1);
前 11 列如下(ObjectIDs 和 PartitionIDs 'X'ed out)
PageFID PagePID IAMFID IAMPID ObjectID IndexID PartitionNumber PartitionID iam_chain_type PageType IndexLevel
1 133075 NULL NULL XXXXXXX 1 1 XXXXX In-row data 10 NULL
1 152432 1 133075 XXXXXXX 1 1 XXXXX In-row data 1 0
1 133077 NULL NULL XXXXXXX 2 1 XXXXX In-row data 10 NULL
1 160528 1 133077 XXXXXXX 2 1 XXXXX In-row data 2 0
1 133078 NULL NULL XXXXXXX 3 1 XXXXX In-row data 10 NULL
1 169849 1 133078 XXXXXXX 3 1 XXXXX In-row data 2 0
从那里开始,最后一行是相关的(它是索引 3 的实际数据)。 第一列 (FileID) 是 1,第二列 (PageID) 是 169849 - 使用这些作为下一个 DBCC 命令的输入(我相信最后一个值 3 代表详细程度)。
DBCC PAGE('Testdb',1,169849,3) WITH TABLERESULTS;
这报告了两个表——第二个表是索引的内容。 它显示索引字段 (TT_IX_Field) 以及聚集索引值 (TT_CX)。
FileId PageId Row Level TT_IX_field (key) TT_CX (key) UNIQUIFIER (key) KeyHashValue Row Size
1 169849 0 0 DKTPE 15 0 (eba1206db4a9) 22
1 169849 1 0 GEWPX 10 0 (0d8521571a6a) 22
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.