用于创建也是主键的聚集索引的SQL Server语法为:
CREATE TABLE dbo.c
(
c1 INT NOT NULL,
c2 INT NOT NULL,
CONSTRAINT PK_c
PRIMARY KEY CLUSTERED (c1, c2)
);
据您的评论:“使PK使用命名索引”,以上代码将导致主键索引被命名为“ PK_c”。
主键和聚类键不必位于同一列。您可以单独定义它们。在以上示例中,将CLUSTERED
关键字更改为NONCLUSTERED
,然后使用以下CREATE INDEX
语法简单地添加聚簇索引:
CREATE TABLE dbo.c
(
c1 INT,
c2 INT,
CONSTRAINT PK_c
PRIMARY KEY NONCLUSTERED (c1, c2)
);
CREATE CLUSTERED INDEX CX_c ON dbo.c (c2);
在SQL Server中,聚集索引是表,它们是相同的。聚集索引定义表中存储的行的逻辑顺序。在我的第一个示例中,行以c1
和c2
列的值顺序存储。由于群集密钥也被定义为主键,因此c1
和的组合在c2
表范围内必须是唯一的。
在第二个示例中,主键由c1
和c2
列组成,但是聚类键只是该c2
列。由于我未UNIQUE
在CREATE INDEX
语句中指定属性,因此c2
不需要集群键()在整个表中是唯一的。SQL Server将自动创建一个“ uniquifier”,并将其附加到该c2
列中的值以创建聚类键。由于该集群键现在是唯一的,因此将在表上创建的其他索引中用作行ID。
为了证明聚类键控制存储中行的布局,可以使用未记录的功能fn_PhysLocCracker(%%PHYSLOC%%)
。以下代码显示了行按磁盘顺序排列在磁盘上的顺序c2
,我将其定义为集群键:
USE tempdb;
CREATE TABLE dbo.PKTest
(
c1 INT NOT NULL
, c2 INT NOT NULL
, c3 VARCHAR(256) NOT NULL
);
ALTER TABLE PKTest
ADD CONSTRAINT PK_PKTest
PRIMARY KEY NONCLUSTERED (c1, c2);
CREATE CLUSTERED INDEX CX_PKTest
ON dbo.PKTest(c2);
TRUNCATE TABLE dbo.PKTest;
INSERT INTO dbo.PKTest (c1, c2, c3)
SELECT TOP(25) o1.object_id / o2.object_id, o2.object_id, o1.name + '.' + o2.name
FROM sys.objects o1
, sys.objects o2
WHERE o1.object_id >0
and o2.object_id > 0;
SELECT plc.file_id
, plc.page_id
, plc.slot_id
, pk.*
FROM dbo.PKTest pk
CROSS APPLY fn_PhysLocCracker(%%PHYSLOC%%) plc;
我的 tempdb 的结果是:
在上图中,fn_PhysLocCracker
函数的前三列是输出,显示了磁盘上行的物理顺序。您可以看到该slot_id
值随c2
值(即聚类键)增加了锁定步长。主键索引以不同的顺序存储行,这可以通过强制SQL Server从扫描主键返回结果来看到:
SELECT pkt.c1
, pkt.c2
FROM dbo.PKTest pkt WITH (INDEX = PK_PKTest, FORCESCAN);
注意,ORDER BY
由于我试图显示主键索引中的项目顺序,因此我没有在上面的语句中使用子句。
上面查询的输出是:
查看该fn_PhysLocCracker
函数,我们可以看到主键索引的物理顺序。
SELECT plc.file_id
, plc.page_id
, plc.slot_id
, pkt.c1
, pkt.c2
FROM dbo.PKTest pkt WITH (INDEX = PK_PKTest, FORCESCAN)
CROSS APPLY fn_PhysLocCracker(%%PHYSLOC%%) plc;
由于我们是专门从索引本身读取的,即查询中没有引用索引外部的列,因此这些%%PHYSLOC%%
值表示索引本身中的页面。
结果:
create table c (c1 int not null primary key, c2 int)