私はオフィスのさまざまな開発者と、インデックスのコスト、および一意性が有益であるか高価であるか(おそらく両方)について継続的な議論を行ってきました。問題の核心は、競合するリソースです。
バックグラウンド
操作はBツリーのどこに収まるかを暗黙的にチェックし、一意でないインデックスに重複が見つかった場合は、一意化を追加するため、インデックスUniqueを維持するための追加コストはないという説明を以前読んだInsertことがありますキーの終わりですが、それ以外の場合は直接挿入します。この一連のイベントでは、Uniqueインデックスに追加コストはありません。
私の同僚Uniqueは、Bツリー内の新しい位置へのシーク後の2番目の操作として強制されるため、このステートメントに対抗します。したがって、一意でないインデックスよりも維持にコストがかかります。
最悪の場合、テーブルのクラスタリングキーであるID列(本質的に一意)を持つテーブルを見たことがありますが、明示的に非一意であると述べられています。最悪の反対側には、一意性への執着があり、すべてのインデックスは一意として作成されます。インデックスに明示的に一意のリレーションを定義できない場合は、テーブルのPKをインデックスの末尾に追加して、一意性が保証されます。
私は開発チームのコードレビューに頻繁に関与しており、彼らが従うための一般的なガイドラインを提供できる必要があります。はい、すべてのインデックスを評価する必要がありますが、それぞれ数千のテーブルとテーブルに最大20のインデックスを持つ5つのサーバーがある場合、特定のレベルの品質を確保するためにいくつかの簡単なルールを適用できる必要があります。
質問
一意性には、Insert非一意のインデックスを維持するコストと比較して、バックエンドで追加コストがかかりますか?第二に、一意性を確保するためにインデックスの最後にテーブルの主キーを追加することの何が問題になっていますか?
テーブル定義の例
create table #test_index
    (
    id int not null identity(1, 1),
    dt datetime not null default(current_timestamp),
    val varchar(100) not null,
    is_deleted bit not null default(0),
    primary key nonclustered(id desc),
    unique clustered(dt desc, id desc)
    );
create index
    [nonunique_nonclustered_example]
on #test_index
    (is_deleted)
include
    (val);
create unique index
    [unique_nonclustered_example]
on #test_index
    (is_deleted, dt desc, id desc)
include
    (val);
例
Uniqueインデックスの最後にキーを追加する理由の例は、ファクトテーブルの1つにあります。そこでPrimary KeyあることIdentity列。ただし、Clustered Index代わりにパーティションスキーム列があり、その後に一意性のない3つの外部キーディメンションが続きます。このテーブルでのパフォーマンスの選択はひどいものであり、をPrimary Key利用するよりもキー検索を使用した方がシーク時間を改善できますClustered Index。同様の設計に従っているがPrimary Key、最後に追加されている他のテーブルは、パフォーマンスが大幅に向上しています。
-- date_int is equivalent to convert(int, convert(varchar, current_timestamp, 112))
if not exists(select * from sys.partition_functions where [name] = N'pf_date_int')
    create partition function 
        pf_date_int (int) 
    as range right for values 
        (19000101, 20180101, 20180401, 20180701, 20181001, 20190101, 20190401, 20190701);
go
if not exists(select * from sys.partition_schemes where [name] = N'ps_date_int')
    create partition scheme 
        ps_date_int
    as partition 
        pf_date_int all 
    to 
        ([PRIMARY]);
go
if not exists(select * from sys.objects where [object_id] = OBJECT_ID(N'dbo.bad_fact_table'))
    create table dbo.bad_fact_table
        (
        id int not null, -- Identity implemented elsewhere, and CDC populates
        date_int int not null,
        dt date not null,
        group_id int not null,
        group_entity_id int not null, -- member of group
        fk_id int not null,
        -- tons of other columns
        primary key nonclustered(id, date_int),
        index [ci_bad_fact_table] clustered (date_int, group_id, group_entity_id, fk_id)
        )
    on ps_date_int(date_int);
go
if not exists(select * from sys.objects where [object_id] = OBJECT_ID(N'dbo.better_fact_table'))
    create table dbo.better_fact_table
        (
        id int not null, -- Identity implemented elsewhere, and CDC populates
        date_int int not null,
        dt date not null,
        group_id int not null,
        group_entity_id int not null, -- member of group
        -- tons of other columns
        primary key nonclustered(id, date_int),
        index [ci_better_fact_table] clustered(date_int, group_id, group_entity_id, id)
        )
    on ps_date_int(date_int);
go
          
CaseとIf構造が10レベルに制限されているのと同様に、一意でないエンティティの解決にも制限があることは理にかなっています。あなたの声明によると、これはクラスタリングキーが一意でない場合にのみ適用されるように聞こえます。これはNonclustered Index、クラスタ化キーが問題である場合、またはインデックスにUnique問題がない場合Nonclusteredですか?