一意のインデックスの最大16列を回避する方法


8

CREATE INDEXドキュメントによると:

最大16列を単一の複合インデックスキーに組み合わせることができます。

一意の組み合わせを形成する必要がある〜18列のテーブルがあります。このテーブルはパフォーマンスの影響を受けません -値を更新したりレコードを挿入したりすることはほとんどありません。レコードが重複しないようにする必要があるだけです。単純な一意性の制約を課すことができると考えました。

何か案は?より良い方法がある場合は、一意のインデックス/制約を完全に回避することにオープンです。


4
それはいくつかのテーブルです。

@Joe:同様のサブタイプを1つに組み合わせた場合、状況によっては珍しくありません。私の場合、50以上の異なるテーブルではなく、15列のキーが必要です。実装の決定...
gbn

あなたが求めていることは可能ですが、それが賢明であるかどうかはわかりません。あなたは暴力的な道をたどっていません。このように、あなたは驚きのためにいます。あなたは他の人よりも自分の過ちで学ぶ傾向があります。長期的には、従来のアプローチを試す方が簡単な場合があります。詳細を投稿していただければ、実装のお手伝いをさせていただきます。
AK

しばらく前から知っていますが、GUID ID列を単純に使用できなかったのはなぜですか。
Robert Harvey

回答:


14

18個のキーを組み合わせた永続的な計算列を追加してから、計算列に一意のインデックスを作成します。

alter table t add all_keys as c1+c2+c3+...+c18 persisted;
create unique index i18 on t (all_keys);

計算列でのインデックスの作成を参照してください

もう1つの方法は、インデックス付きビューを作成することです。

create view v 
with schemabinding
as select c1+c2+c3+...+c18 as all_keys
from dbo.t;

create unique clustered index c18 on v(all_keys);

インデックス付きビューの作成を参照してください。

どちらのアプローチでも部分的なキー集計が可能です。c1+ c2 + c3をk1、c4 + c5 + c6をk2として集計し、(k1、k2、...)でインデックス付きビューをインデックス付け/作成します。チアは、範囲スキャンに役立つ可能性があります(インデックスはc1 + c2 + c3の検索に使用できます。

もちろん、+私の例のすべての操作は文字列集計です。実際に使用する演算子は、それらのすべての列のタイプによって異なります(つまり、明示的なキャストを使用する必要がある場合があります)。

PS。一意の制約は一意のインデックスによって適用されるため、一意のインデックスに対する制限は一意の制約にも適用されます。

create table t (
    c1 char(3), c2 char(3), c3 char(3), c4 char(3),
    c5 char(3), c6 char(3), c7 char(3), c8 char(3),
    c9 char(3), c10 char(3), c11 char(3), c12 char(3),
    c13 char(3), c14 char(3), c15 char(3), c16 char(3),
    c17 char(3), c18 char(3), c19 char(3), c20 char(3),
    constraint unq unique
      (c1,c2,c3,c4,c5,c6,c7,c8,c9,c10,c11,c12,c13,c14,c15,c16,c17,c18));
go  


Msg 1904, Level 16, State 1, Line 3
The index '' on table 't' has 18 column names in index key list. 
The maximum limit for index or statistics key column list is 16.
Msg 1750, Level 16, State 0, Line 3
Could not create constraint. See previous errors.

ただし、永続的な計算列に制約を作成すると機能します。

create table t (
    c1 char(3), c2 char(3), c3 char(3), c4 char(3),
    c5 char(3), c6 char(3), c7 char(3), c8 char(3),
    c9 char(3), c10 char(3), c11 char(3), c12 char(3),
    c13 char(3), c14 char(3), c15 char(3), c16 char(3),
    c17 char(3), c18 char(3), c19 char(3), c20 char(3),
    all_c as 
        c1+c2+c3+c4+c5+c6+c7+c8+c9+c10+c11+
        c12+c13+c14+c15+c16+c17+c18 
        persisted
        constraint unq unique (all_c));
go  

明らかに、永続化された列はディスク上のスペースを消費するため、非常に大きなテーブルではアプローチが不適切になる場合があります。インデックス付きビューのアプローチにはこの問題はありません。計算された列インデックスのスペースではなく、インデックスのスペースを消費するだけです。


1
もちろん900バイトのインデックスキーの制限に
注意してください

1
@gbnはい、そしてそれが私がRBarryYoungによって提案されたHashBytes関数を使用することになった理由です。しかし、私はこの回答を受け入れました。なぜなら、それはさまざまな方法の説明と探求のより多くを提供したからです。(つまり、ここで多くのことを学びました)
Nick B

13

HASHBYTES('MD5', ...)18個の列の組み合わせを使用して生成された計算列に一意のインデックスチェックを配置する方がはるかに良いと思います。


2

私はこの問題に遭遇し、私の上級DBAは一意性チェック機能の使用を提案しました。私の挿入は比較的小さく、頻度が低く(約1000行、毎月の初めに挿入)、唯一の懸念は一意性の強制です。

CREATE FUNCTION dbo.fn_UQ_table1 ()  
RETURNS BIT

AS
BEGIN
      DECLARE @ResultBit BIT = 1

      IF EXISTS(
      SELECT COUNT(*)
      FROM [table1]
      GROUP BY [c1],[c2],[c3],[c4],[c5],[c6],
            [c7],[c8],[c9],[c10],[c11],[c12],
            [c13],[c14],[c15],[c16]
      HAVING COUNT(*) > 1)
      SELECT @ResultBit = 0

      RETURN      @ResultBit

END

SELECT dbo.fn_UQ_table1()

ALTER TABLE [table1]  
WITH NOCHECK ADD  
CONSTRAINT [CK_UQ] CHECK  (([dbo].[fn_UQ_table1]()=1))

@RBarryYoung、まだコメントする担当者がいませんが、データ型の1つが日時だったためHASHBYTESソリューションに問題があり、オプションのスタイル引数を提供していないという初心者(?)の間違いを犯しましたvarcharに変換するときのCONVERT関数。スタイルがないと、PERSISTED UNIQUE NONCLUSTERED制約を追加しようとすると次のエラーが発生します。

"column 'key_hash' in table 'table1' cannot be persisted because 
the column is non-deterministic."

0

いくつかの値を組み合わせて、新しい一意の値を作成し、現在のデータに加えてそれを保存することができます。

新しい値を作成するユーザー定義関数を作成し、データが追加されたときにフィールドにデータを入力するトリガーを作成すると、フィールドのメンテナンスにかかるオーバーヘッドが大幅に減ります。

2つまたは3つのフィールドを組み合わせると、16の制限を下回ります。


-1列数を減らすためにテーブルを非正規化するという考えには同意しません。
Matt M

@Matt M-この質問に対する承認された回答の最初の提案とあまり変わらないのに、なぜあなたが私の回答に反対票を投じたのか知りたいのですが。また、なぜあなたが反対するのか知りたいのですが、あなたの解決策は何ですか?
Tony

実際、あなたの提案は実際には、受け入れられている解決策とは異なります。受け入れられたソリューションは、結合された値を含む新しい列を作成することを提唱しているのに対し、列を結合することを提唱しています。このソリューションでは、複雑なクエリを使用して、有用なデータを結合された列から分割することで、パフォーマンスの問題が発生する可能性があります。個人的には、RBarryYoungによって提示された、一意のインデックスに配置されたHashBytes PERSISTED計算列の組み合わせを利用するソリューションを提唱します。逆に、私は彼の解決策を支持しました。
Matt M

@Matt M-説明ありがとうございますが、「...新しい一意の値を作成し、現在のデータに加えて保存します」と言いました。新しいキー列は、既存のデータを補完するものであり、それを置き換えるものではない新しいフィールドになるように意図しました。永続的な計算フィールドの使用は、UDFの提案よりも優れていることに同意しますが、精神的には、私の解決策は同じでした。
Tony

私はあなたの解決策を誤って読んだようです、そして私はそれをお詫びします。そうは言っても、いくつかの列を組み合わせるのは、私の意見では、HashBytesソリューションほど良いソリューションではありません。私は-1を撤回します。もう一度、私は私の読解力をお詫び申し上げます。
Matt M

0

insert/のトリガーで行くことができupdateます。の句を使用して、列ごとにグループ化を選択しますhaving count(*) > 1。それが空ではない場合は、ロールバックします。


0

これが私がすることです。INSERT、UPDATEのROW_NUMBER ()機能を実行するAFTERトリガーを作成し、一意の18列すべてでパーティション化します。最大行数が1より大きい場合は、を実行しROLLBACKます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.