SQL Server 聚集索引键应该是非聚集索引键的一部分,但如何查看它?

weylhg0b  于 2022-12-17  发布在  其他
关注(0)|答案(1)|浏览(167)

在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”,因为它是聚集索引键。
有没有办法可以看出此列实际上是非聚集索引的一部分?

dldeef67

dldeef671#

查看它的一种方法是从一个简单的表执行SELECT,然后查看它使用了哪些索引。
在下面的设置中,我

  • C1作为主键,但不聚集
  • C2作为聚集索引,
  • C3作为具有非聚集索引的列
  • C4是一个只占用一些空间的列(使聚集索引更加笨拙)

(Note- 我使用一个数字表用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;并打开“Include actual execution plan”,然后执行以下命令

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获取TOP10,所以使用非聚集索引很有用。
在第一个示例中,它只需要读取非聚集索引,因为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/的-但请注意,您似乎不再需要设置traceflag 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列如下所示(对象ID和分区ID用“X”标出)

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

相关问题