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


11

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]ます。

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

[CODE]列の値は一意でなければならないので、UNIQUEその列に制約を設定できると思っていました。

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


1
考慮すべき非常に重要なことは、テーブルにいくつの行があるかということです。
ジェームズZ

これ自体は答えではありませんが、CODE列は一意である必要がありますが、主キーではないように考える傾向があります。情報が載っていると思います。その情報が何らかの方法で変更可能な場合、CODE変更するか、古くする必要があります。それはあなたの主キーを不安定にするでしょう、そして私はそれがうまく終わるのを見ることができません。PKをキーとして使用することをお勧めします。コードは、好きなように実行できます。ただの意見。
マンゴ2018年

@Manngo、コメントありがとうございます。はい、そのようにしました。ID_CODEが主キーで、CODEがUNIQUEです。
VansFannel 2018年

回答:


13

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

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

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

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

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

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

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


更新
この回答に関するコメントで、この質問に基づくもう少し詳しい情報:

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

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

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

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

  • [CODE]値について:

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

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

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


匿名のダウン投票者(@noIDonthissystemの回答にも反対投票したようです)は、建設的な批判を提供したり、欠陥のあるロジックを指摘したりしますか?
ソロモン・ルツキー

ご回答有難うございます。である[ID_CODE]ように、PRIMARY KEY私が使用している場合は最良の選択肢、[CODE]テーブルをルックアップするために列を?
VansFannel 2015

@VansFannel私のアップデートを見てください。ありがとう。
ソロモンルツキー2015

私はこのdbaコミュニティに参加して、この回答に賛成票を投じました。
アフメットアルスラン

6

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

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

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

主キーがクラスター化インデックスである必要はありません。あなたはID_CODEPKとして持つことができます(CODE_LEVEL, CODE)クラスター化されたキーとしてます。またはその逆。

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

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

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

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


ご回答有難うございます。である[ID_CODE]ように、PRIMARY KEY私が使用している場合は最良の選択肢、[CODE]コラム(そしておそらく[CODE_LEVEL]、テーブルをルックアップするために)を?
VansFannel 2015

@VansFannel あなただけがそれに答えることできます。
Remus Rusanu 2015

しかし、あなたの意見では...
VansFannel 2015

2
私の意見では、テーブル全体とすべてのインデックスの正確なDDL、それを参照する外部キー、推定行数、予想されるクエリのワークロード、アプリケーションが予想するSLAを考慮する必要があります。
Remus Rusanu 2015

ありがとう。[CODE]columnをPRIMARY KEYとして使用します。
VansFannel 2015

4

私は誰も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)億を超える通常の制限で十分ではないでしょうか。


SQLではintは4バイトなので、-21億から+21億になります。
datagod 2015

@datagod、ありがとうございます、私が間違って数えた桁が多すぎます!
にこのシステムにIDがない

ご回答有難うございます。である[ID_CODE]ように、PRIMARY KEY私が使用している場合は最良の選択肢、[CODE]テーブルをルックアップするために列を?ありがとう。
VansFannel 2015

私は、誰かが "int"のシーケンシャルな性質を使用してDBのデータ/ユーザーを予測し、私が持っていたほとんどすべてのものを収穫するまで、このボートにいました。二度と。一般向けDBは、情報を取得するのが少し難しい必要があります。
DaBlue、

3

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

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

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

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


2
実際、NVARCHAR(20)はサイズが40バイト(最大)であり、可変長の列であるため、クラスター化インデックスに最適ではありません。ID_CODEことはBIGINT IDENTITYだろうより良いここの選択!
marc_s 2015

40バイトであることはわかっていますが、900バイトに近いため、指定する理由はあまりありませんでした。また、主にCODEからデータをクエリする場合は、インデックスを維持するために冗長なインデックスを持たない方がよいでしょう。インデックスがまだ必要であり、クラスター化された後方
Allan S)

言及する価値がある-私が言及するのを忘れていて、@ marc_sが対処しているのは私が疑っているのは、このタイプのインデックスは、順次IDよりも大きなインデックスの断片化につながる可能性があることですが、クエリ要素について。
アランS.ハンセン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.