相当于 Oracle USING INDEX 子句的 SQL Server

nik*_*nik 9 sql-server-2008 oracle sql-server constraint primary-key

是否有 SQL Server 2008 等效于 Oracle 中的 USING INDEX 子句?特别是对于构造:

CREATE TABLE c(c1 INT, c2 INT);
CREATE INDEX ci ON c (c1, c2);
ALTER TABLE c ADD CONSTRAINT cpk PRIMARY KEY (c1) USING INDEX ci;
Run Code Online (Sandbox Code Playgroud)

关于唯一索引的 Sql Server文档中,它指出(强调):

唯一索引通过以下方式实现:

PRIMARY KEY 或 UNIQUE 约束

当您创建 PRIMARY KEY 约束时,如果表上的聚集索引尚不存在并且您没有指定唯一的非聚集索引,则会自动创建一个或多个列上的唯一聚集索引。主键列不能允许 NULL 值。

这似乎意味着有一种方法可以指定主键应该使用什么索引。

Pau*_*ite 7

是否有 SQL Server 2008 等效于 Oracle 中的 USING INDEX 子句?

否。当您在 SQL Server 中创建主键或唯一约束时,会自动创建一个唯一索引来支持该约束,并使用相同的键。

这似乎意味着有一种方法可以指定主键应该使用什么索引。

否。如果您未指定,该文档只是试图解释自动支持索引是否将创建为集群或非集群。措辞令人困惑,我同意。

澄清一下,当您向现有表添加主键约束而不表示偏好时,如果表上没有预先存在的聚集索引,则支持索引将被聚集。如果已经存在聚集索引,则支持索引将创建为非聚集索引

您可以使用:PRIMARY KEY CLUSTERED或专门请求集群或非集群主键PRIMARY KEY NONCLUSTERED

公平地说,有关该主题的文档更加清晰:

table_constraint (Transact-SQL)


Han*_*non 5

用于创建也是主键的聚集索引的 SQL Server 语法是:

CREATE TABLE dbo.c
(
    c1 INT NOT NULL, 
    c2 INT NOT NULL,
    CONSTRAINT PK_c
    PRIMARY KEY CLUSTERED (c1, c2)
);
Run Code Online (Sandbox Code Playgroud)

至于您的评论:“使 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);
Run Code Online (Sandbox Code Playgroud)

在 SQL Server 中,聚集索引就是表,它们是一模一样的。聚集索引定义存储在表中的行的逻辑顺序。在我的第一个示例中,行按照c1c2列的值的顺序存储。由于聚集键也被定义作为主键的组合,c1并且c2必须是唯一的表范围。

在第二个示例中,主键由c1c2列组成,但集群键只是c2列。由于我没有UNIQUECREATE 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;
Run Code Online (Sandbox Code Playgroud)

我的tempdb的结果是:

在此处输入图片说明

在上图中,前三列是fn_PhysLocCracker函数的输出,显示了磁盘上行的物理顺序。可以看到该slot_id值与该值增加了锁步,该c2值是聚类键。主键索引以不同的顺序存储行,可以通过强制 SQL Server 返回扫描主键的结果来看到:

SELECT pkt.c1
    , pkt.c2
FROM dbo.PKTest pkt WITH (INDEX = PK_PKTest, FORCESCAN);
Run Code Online (Sandbox Code Playgroud)

请注意,我没有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;
Run Code Online (Sandbox Code Playgroud)

由于我们只从索引本身读取数据,即查询中没有引用索引之外的列,因此这些%%PHYSLOC%%值代表索引本身中的页面。

结果:

在此处输入图片说明