web-dev-qa-db-ja.com

PRIMARY KEYまたはUNIQUE列としてのNVARCHAR列

SQL Server 2012データベースを開発していますが、nvarchar列を主キーとして使用することに疑問があります。

私はこのテーブルを持っています:

CREATE TABLE [dbo].[CODES]
(
    [ID_CODE] [bigint] IDENTITY(1,1) NOT NULL,
    [CODE_LEVEL] [tinyint] NOT NULL,
    [CODE] [nvarchar](20) NOT NULL,
    [FLAG] [tinyint] NOT NULL,
    [IS_TRANSMITTED] [bit] NOT NULL DEFAULT 0,
     CONSTRAINT [PK_CODES] PRIMARY KEY CLUSTERED 
    (
        [CODE_LEVEL] ASC,
        [CODE] ASC
    )
)

しかし、今度は[CODE]列を主キーとして使用し、[ID_CODE]列を削除します。

PRIMARY KEYとしてNVARCHAR列がある場合、問題やペナルティはありますか?

[CODE]列の値は一意である必要があるため、その列にUNIQUE制約を設定できると考えました。

[CODE]を主キーとして使用する必要がありますか、それとも[CODE]列にUNIQUE制約を設定した方が良いですか?

11
VansFannel

はい、主キーに数値型の代わりに文字列を使用することにはマイナスの影響があり、そのPKがクラスター化されている場合(実際にはそうです)の場合はさらにマイナスになります。ただし、文字列フィールドを使用した場合の影響の程度は、a)このテーブルに含まれる行数、およびb)他のテーブルに含まれる行数がこのPKの外部キーである関数です。このテーブルに1万行しかなく、他のいくつかのテーブルに100万行しかない場合、そのフィールドを介してこのテーブルにFKすると、おそらくそれほど目立たなくなります。ただし、行数が増えると、これらの影響は確実に顕著になります。

クラスタ化インデックスのフィールドが非クラスタ化インデックスに引き継がれることを考慮する必要があります。つまり、1行あたり最大40バイトを表示するだけでなく、(40 * some_number)バイトも表示します。そして、どのFKテーブルでも、行に同じ40バイトがあり、JOINで使用されているため、そのフィールドに非クラスター化インデックスがあるため、FKを実行するすべてのテーブルで実際に2倍になりますこれです。 40バイト* 100万行*それの10コピーは心配する必要がないと考える傾向がある場合は、私の記事を参照してください Disk Is Cheap!ORLY? 詳細すべて(または少なくともほとんど) )この決定によって影響を受ける領域の。

考慮すべき他のことは、特にバイナリ照合を使用しない場合(通常、大文字と小文字を区別しないデータベースのデフォルトを使用していると想定)、文字列のフィルタリングとソートは、INTを使用する場合よりもはるかに効率が悪い(つまり時間がかかる)ことです。 BIGINT。これは、このフィールドでフィルタリング/結合/ソートするすべてのクエリに影響します。

したがって、CHAR(5)のようなものを使用しても、クラスター化されたPKにはおそらくOKですが、ほとんどがCOLLATE Latin1_General_100_BIN2(またはそのようなもの)でも定義されている場合はそうです。

そして、[CODE]の値は変化する可能性がありますか?はいの場合は、それをPKとして使用しない理由です(FKをON UPDATE CASCADEに設定した場合でも)。変更できない場合や変更できない場合は問題ありませんが、それをクラスター化PKとして使用しない理由はまだ十分にあります。

もちろん、現在PKにこのフィールドがすでにあるように見えるため、質問のフレーズが誤っている可能性があります。

とにかく、最善のオプションは、はるかに、クラスター化PKとして[ID_CODE]を使用し、関連するテーブルのそのフィールドをFKとして使用し、[CODE]UNIQUE INDEXとして保持することです(つまり、 「代替キー」です)。


更新
この質問に基づいたもう少し詳しい情報がこの回答のコメントにあります:

[ID_CODE]は、PRIMARY KEYとして、[CODE]列を使用してテーブルを検索する場合の最良のオプションですか?

これはすべて、非常に多くの要因に依存します。そのいくつかは、すでに述べましたが、以下に述べます。

主キーは、外部キーによって参照されているかどうかに関係なく、個々の行を識別する方法です。システムが行を内部的に識別する方法は、ユーザーが自分やその行を識別する方法に関連していますが、必ずしも同じであるとは限りません。一意のデータを持つすべてのNOT NULL列は機能しますが機能しますが、特にPKが実際にFKによって参照されている場合は、考慮すべき実用上の問題があります。たとえば、GUIDは一意であり、さまざまな理由で実際にGUIDを使用することを好む人もいますが、それらはクラスター化インデックスには非常に適していません(NEWSEQUENTIALIDの方が適していますが、完全ではありません)。一方、GUIDは代替キーとしては問題なく、アプリが行を検索するために使用しますが、JOINはINT(または同様の)PKを使用して実行されます。

これまでのところ、[CODE]フィールドがどのようにシステムに適合するかをすべての角度から説明していませんが、これは行をルックアップする方法であると述べた以外に、すべてのクエリまたは一部のものについてですか?したがって:

  • [CODE]値について:

    • どのように生成されますか?
    • インクリメンタルですか、それとも擬似ランダムですか?
    • 均一な長さですか、それともさまざまな長さですか?
    • 使用されている文字は?
    • アルファベット文字を使用する場合:大文字と小文字が区別されますか、それとも区別されますか?
    • 挿入後に変更することはできますか?
  • この表について:

    • 他のテーブルはこのテーブルに対してFKを行いますか?または、これらのフィールド([CODE]または[ID_CODE])は、明示的に外部キー化されていなくても、他のテーブルで使用されていますか?
    • If[CODE]が個々の行を取得するために使用される唯一のフィールドである場合、[ID_CODE]フィールドはどのような目的に役立ちますか?それが使用されない場合、なぜそれが最初にあるのですか(「[CODE]フィールドは変更できるか」に対する回答に依存する可能性があります)?
    • このテーブルの行数は?
    • 他のテーブルがこのテーブルを参照する場合、それぞれにいくつの行が含まれていますか?
    • このテーブルのインデックスは何ですか?

この決定は、「NVARCHARはいまたはいいえ」の質問だけで行うことはできません。もう一度言いますが、一般的に言って、それは良い考えではないと思いますが、それでも大丈夫な場合があります。このテーブルのフィールドが非常に少ないことを考えると、インデックスがこれ以上ないか、少なくともそれほど多くない可能性があります。したがって、どちらの方法でも、クラスター化インデックスとして[CODE]を使用できます。そして、他のテーブルがこのテーブルを参照していない場合は、それをPKにすることもできます。ただし、他のテーブルがこのテーブルを参照している場合は、クラスター化されていない場合でも、[ID_CODE]フィールドをPKとして選択します。

13
Solomon Rutzky

概念を分離する必要があります。

  • 主キーは、デザインの概念であり、テーブル内のエントリの論理プロパティです。テーブルエントリの存続期間中は不変である必要があり、アプリケーションでエントリを参照するために使用されるキーである必要があります。

  • クラスター化インデックスは、ストレージの概念であり、物理的なプロパティです。これは、クエリの最も一般的なアクセスパスである必要があり、ほとんどの場合、インデックスをカバーするように機能し、できるだけ多くの範囲クエリを満たす必要があります。

主キーがクラスター化インデックスである必要はありません。 _ID_CODE_をPKとして、_(CODE_LEVEL, CODE)_をクラスター化キーとして使用できます。またはその逆。

より大きなキーは、インデックスページの密度が低く、すべての非クラスター化インデックスで消費されるサイズが大きいことを意味するため、クラスター化されたキーが大きくなると、いくつかの否定的な影響があります。このトピックについては、すでに大量のインクが流出しています。開始 クラスタリングキーに関するその他の考慮事項–クラスタ化インデックスの議論は続きます!

しかし、問題の要点は、クラスター化インデックスキーの選択は主にトレードオフであることです。一方で、ストレージサイズの要件があり、パフォーマンスに一般的な影響があります(キーが大きい->サイズが大きい-> IOが大きく、IO帯域幅はおそらくですリソースが最も少ないリソース)一方、スペース節約の名前で間違ったクラスター化キーを選択すると、クエリのパフォーマンスに影響を与える可能性があり、多くの場合、ワイドキーから生じる問題よりも悪い結果になります。

主キーの選択に関しては、それは問題であってはなりません。データモデル、アプリのロジックが、主キーが何であるかを指示する必要があります。

そうは言っても、私の2c:NVARCHAR(20)not幅です。大きなテーブルの場合でも、完全に許容できるクラスタ化キーサイズです。

6
Remus Rusanu

私は、データベースでnvarchar(20)をPKにすることを誰にも許可しません。ディスク領域とキャッシュメモリを浪費します。このテーブルのすべてのインデックスとそのテーブルへのすべてのFKは、この広い値を複製します。正当化できるのであれば、char(20)かもしれません。どのようなデータをCODEに保存しようとしていますか? nvarchar文字を本当に保存する必要がありますか?私はPKをユーザーに表示されない「内部」値にする傾向があり、表示される値を別々に保つようにしています。表示される値を変更する必要がある場合があり、これはPK + FKで非常に問題になります。

また、「bigint identity(1,1)」が最大9,223,372,036,854,775,807まで増加できることをご存知ですか?

_[ID_CODE] [bigint] IDENTITY(1,1)
_

Google用にこのデータベースを構築しているのでない限り、20億を超える通常のint identity (1,1)で十分ではないでしょうか?

Nvarchar/varcharを使用している場合、知らない場合はワイドキーを使用するリスクがあること以外は、固有の/注目すべきペナルティはありません。特に、複合キーでそれらを組み合わせ始めた場合。

しかし、あなたの(20)長さの例では、大丈夫で、私はそれについてあまり心配しません。 CODEが主にデータを照会する方法である場合、そのクラスター化インデックスは非常に賢明に聞こえるからです。

ただし、実際にそれを主キーとして使用するのか、それとも一意の(クラスター化)インデックスとして使用するのかを検討する必要があります。クラスター化インデックスと主キーの間には(小さな)違いがあります(基本的に-主キーはデータを識別しますが、インデックスはデータのクエリ方法です)。したがって、ID_Codeを主キーと同じくらい簡単に作成でき、 CODEを介して一意のクラスター化インデックスを作成します。 (注意:SQL Serverは自動的に主キーをクラスター化インデックスにしますnlessクラスター化インデックスを手動で作成した場合)

また、ID_Codeが実際に必要かどうかを検討し、一意のCODEを取得します。

3
Allan S. Hansen