主キーでもあるクラスター化インデックスを作成するための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
必要があります。
2番目の例では、主キーはc1
とc2
列で構成されていますが、クラスタリングキーは単なるc2
列です。ステートメントでUNIQUE
属性を指定しなかったためCREATE INDEX
、クラスタリングキー(c2
)はテーブル全体で一意である必要はありません。「一意化」はSQL Serverによって自動的に作成され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が、以下のとおりです。
上の画像では、最初の3列が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)