在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”,而我希望包含“col1”,因为它是聚集索引键。
有没有办法可以看出此列实际上是非聚集索引的一部分?
1条答案
按热度按时间dldeef671#
查看它的一种方法是从一个简单的表执行SELECT,然后查看它使用了哪些索引。
在下面的设置中,我
(Note- 我使用一个数字表用10,000行填充临时表)。
现在,运行
SET STATISTICS TIME, IO ON;
并打开“Include actual execution plan”,然后执行以下命令第一个(SELECTING C3,C2)只对非聚集索引执行索引扫描- #IX_Test -显示聚集列也包含在非聚集索引中。
另一方面,第二个(SELECTING C3,C1)执行聚集索引扫描,以获取C1字段中所需的信息--尽管在本例中,它是主键,但是,由于它不是非聚集索引的一部分,它必须从更笨拙的聚集索引中获取信息(相应地,逻辑读取次数比第一个高8倍)。
最后,您可以执行另一种类型的测试
这些索引与上面的类似,但是因为它只使用ORDER BY C3获取TOP10,所以使用非聚集索引很有用。
在第一个示例中,它只需要读取非聚集索引,因为C2也包含在非聚集索引中。
在第二个示例中,它需要执行键查找以获取C1数据,因为它 * 不 * 包含在非聚集索引中。
最后,如果我删除聚集索引(所以现在表没有聚集索引-它只是一个堆)并再次运行最后的语句
这些计划彼此相同-从非聚集索引阅读,然后执行RID查找(例如,使用Row_ID而不是聚集索引)以获取其他字段。
删除聚集索引实际上更改了非聚集索引的内容,但其定义没有实际更改。
编辑:这里有一个有点违反直觉的例子,使用最初的索引(在删除聚集索引之前),运行以下代码。
在这种情况下,它从非聚集索引中读取数据,尽管在索引(C3)中实际指定的字段实际上并不在SELECT语句中。SQL Server只是问自己“我应该从聚集索引(有很多额外的读取)还是较小的非聚集索引中获取数据?”-显然,它决定非聚集索引是一条可行之路。
更新-数据法
在注解中,OP指定他们想要一个通过元数据的方法。
我一直无法获得元数据方法,但我找到了一种方法,您可以使用DBCC直接查看实际数据,这显示聚集索引已包含在内。
这是基于https://www.mssqltips.com/sqlservertip/1578/using-dbcc-page-to-examine-sql-server-table-and-index-data/的-但请注意,您似乎不再需要设置traceflag 3604。
为此,我将在数据库
Testdb
中创建一个新表TestTab
,设置与上面类似(非聚集主键、单独的聚集索引、非聚集索引的一个字段和另一个misc字段)。现在,第一个命令是获取相关的页面信息
前11列如下所示(对象ID和分区ID用“X”标出)
从这里开始,最后一行是相关的(它是索引3的实际数据),第一列(FileID)是1,第二列(PageID)是169849 -使用这些作为下一个DBCC命令的输入(我相信最后一个值3代表详细程度)。
这将报告两个表--第二个表是索引的内容。它显示索引字段(TT_IX_Field)以及聚集索引值(TT_CX)。