一意性を確保する主キーがすでにあると仮定しましょう。私の主キーは、レコードのインデックスの順序付けでもあります。ただし、ディスク内のレコードの物理的な順序(存在する場合)での主キーのタスクに興味があります。そして実際の質問は、これらのレコードに個別のクラスター化されたインデックスを作成できるかどうかです。
2 に答える
これは、 @Catcall との議論に従って、クラスタ化されたテーブルのカバリングセカンダリ インデックスのサイズとパフォーマンス特性をテストする試みです。
すべてのテストは、MS SQL Server 2008 R2 Express (かなり能力の低い VM 内) で行われました。
サイズ
まず、セカンダリ インデックスを使用してクラスター化されたテーブルを作成し、いくつかのテスト データを入力しました。
CREATE TABLE THE_TABLE (
FIELD1 int,
FIELD2 int NOT NULL,
CONSTRAINT THE_TABLE_PK PRIMARY KEY (FIELD1)
);
CREATE INDEX THE_TABLE_IE1 ON THE_TABLE (FIELD2) INCLUDE (FIELD1);
DECLARE @COUNT int = 1;
WHILE @COUNT <= 1000000 BEGIN
INSERT INTO THE_TABLE (FIELD1, FIELD2) VALUES (@COUNT, @COUNT);
SET @COUNT = @COUNT + 1;
END;
EXEC sp_spaceused 'THE_TABLE';
最後の行で次の結果が得られました...
name rows reserved data index_size unused
THE_TABLE 1000000 27856 KB 16808 KB 11008 KB 40 KB
したがって、インデックスの B ツリー (11008 KB) は、実際にはテーブルの B ツリー (16808 KB) よりも小さくなります。
スピード
テーブル内のデータの範囲内で乱数を生成し、それをテーブルから行全体を選択する基準として使用しました。これを 10000 回繰り返し、合計時間を測定しました。
DECLARE @I int = 1;
DECLARE @F1 int;
DECLARE @F2 int;
DECLARE @END_TIME DATETIME2;
DECLARE @START_TIME DATETIME2 = SYSDATETIME();
WHILE @I <= 10000 BEGIN
SELECT @F1 = FIELD1, @F2 = FIELD2
FROM THE_TABLE
WHERE FIELD1 = (SELECT CEILING(RAND() * 1000000));
SET @I = @I + 1;
END;
SET @END_TIME = SYSDATETIME();
SELECT DATEDIFF(millisecond, @START_TIME, @END_TIME);
最後の行は、181.3 ミリ秒の (10 回の測定の) 平均時間を生成します。
クエリ条件を: に変更するとWHERE FIELD2 = ...
、セカンダリ インデックスが使用されるため、平均時間は 195.2 ミリ秒になります。
実行計画:
したがって、パフォーマンス (PK を選択する場合とカバーするセカンダリ インデックスを選択する場合) は似ているようです。はるかに大量のデータの場合、セカンダリ インデックスの方が少し高速になる可能性があると思います (よりコンパクトでキャッシュに適しているように見えるため) が、テストではまだヒットしませんでした。
弦の測定
varchar(50)
as type forFIELD1
を使用しFIELD2
、長さが 22 ~ 28 文字の文字列を挿入すると、同様の結果が得られました。
サイズは次のとおりです。
name rows reserved data index_size unused
THE_TABLE 1000000 208144 KB 112424 KB 95632 KB 88 KB
また、平均タイミングは、 on の検索で254.7 ms、 FIELD1
fir で 296.9 msFIELD2
でした。
結論
クラスター化されたテーブルにカバリング セカンダリ インデックスがある場合、そのインデックスは、テーブル自体と同様の空間と時間の特性を持ちます (多少遅くなる可能性がありますが、それほどではありません)。効果があれば、データを異なる方法でソートする 2 つの B ツリーが作成されますが、それ以外は非常に類似しており、「2 番目のクラスター」を作成するという目標を達成できます。
それはあなたのdbmsに依存します。それらのすべてがクラスター化インデックスを実装しているわけではありません。そうする人は、さまざまな方法でそれらを実装する傾向があります。私の知る限り、クラスター化インデックスを実装するすべてのプラットフォームは、クラスター化インデックスに含まれる列を選択する方法も提供しますが、主キーがデフォルトであることがよくあります。
SQL Server では、このように非クラスター化主キーと個別のクラスター化インデックスを作成できます。
create table test (
test_id integer primary key nonclustered,
another_column char(5) not null unique clustered
);
Oracle でこれに最も近いのは、索引構成表だと思います。私は間違っているかもしれません。これは、SQL Server でクラスター化インデックスを使用してテーブルを作成することとまったく同じではありません。
SQL Server の 1 つのテーブルに複数のクラスター化インデックスを設定することはできません。テーブルの行は、一度に 1 つの順序でのみ格納できます。実際には、複数の異なる順序で行を格納できると思いますが、順序ごとにテーブルのすべてまたは一部を基本的に複製する必要があります。(この回答を書いた時点では知りませんでしたが、DB2 UDB は複数のクラスター化インデックスをサポートしており、かなり古い機能です。その設計と実装は SQL Server とはかなり異なります。)
主キーの役割は、一意性を保証することです。多くの場合、その作業は主キー列に一意のインデックスを作成することによって行われますが、厳密に言えば、一意性とインデックス作成は 2 つの異なる目的を持つ 2 つの異なるものです。一意性はデータの整合性を目的としています。索引付けは速度を目的としています。
主キーの宣言は、ディスク上の行の順序に関する情報を提供することを意図したものではありません。実際には、通常、ディスク上のインデックス エントリの順序に関する情報が得られます。(主キーは通常、一意のインデックスを使用して実装されるためです。)
クラスター化インデックスを持つテーブルから行を SELECT する場合でも、行がディスクに格納されているのと同じ順序でユーザーに返されるという保証はありません。大まかに言えば、クラスター化インデックスは、クエリ オプティマイザーがより速く行を見つけるのに役立ちますが、それらの行がユーザーに返される順序は制御しません。行がユーザーに返される順序を保証する唯一の方法は、明示的なORDER BY
節を使用することです。(これはかなり頻繁に混乱を招くポイントのようです。多くの人は、クラスタ化されたインデックスに対するそのままの SELECT が期待どおりの順序で行を返さないことに驚いているようです。)