3

Primary-Key(PK)データベースで、 isAUTO_INCREMENT型 のテーブルをたくさん見ました。

Children次のように作成されたテーブルがあるとします。

CREATE TABLE Children(
  childNo INTEGER AUTO_INCREMENT NOT NULL PRIMARY KEY,
  name    VARCHAR(25),
  age     INTEGER,
  address VARCHAR(100) 
)
  • ChildNoですがAUTO_INCREMENT、行を挿入したら、(名前の)子に割り当てられた値をどのように知ることができますか?と悪い選択PK

  • 子供の名前で検索すると、非効率になります(一意であるとは限りません)。AUTO_INCREMENTこのため、主キーとして保持することは、弱いスキーマ設計を意味すると思いますか?

  • 別のテーブルがあり、そこにとしてParents保持する必要があるとします。それならそれは複雑になるでしょう。 ChidNoForeign Key (FK)

  • 再帰的な関連付けがある場合、PKをAUTO_INCREMENTに保つことは非常に悪いことです。

自動インクリメントフィールドをリレーションに保持することは、正規化が適切でないことを示しますか?

一部の表では、代わりに追加のAUTO_INCREMENTフィールドを導入するために、すべての列をPKとして保持したいと思います。私が間違っている?

私の考えは使用に反対ですので、フィールドAUTO_INCREMENTを維持するためのユーザビリティも教えてください。AUTO_INCREMENT

4

2 に答える 2

3

自動インクリメントPK列は、代理キーと呼ぶことができます。

代理キーを使用すると、場合によっては最適化に役立つことがあります。

  • テーブル内の他の列のセットが候補キーとして確実に処理できない場合。あなたの例では、(名前、年齢、住所)の組み合わせが、すべての場合に行を一意に識別することが保証されているとは言えない場合があります。同じ住所に同じ名前、同じ年齢の2人が住んでいる可能性は低いと思われるかもしれません。しかし、それが起こることはまだ無効ではありません。サロゲートキーを使用すると、このような場合に他のすべての列を一意にしないことができます。
  • PKが不変であることが望ましい場合があります。たとえば、人は名前を変更できますが、それでも同じ人です。もちろん、SQLではPK値を変更できますが、値でPKを参照する他のすべてのデータも変更する必要があります。RDBMSがONUPDATECASCADEで外部キーをサポートしている場合は、これを自動化できます。しかし、ON UPDATE CASCADE(Oracleなど)がない場合、外部キー(古いMySQLやSQLiteなど)がない場合、またはRDBMSの外部にデータが保存されている場合はどうなりますか?サロゲートキーを使用すると、行のIDを変更せずに、「自然な」データ列の値を自由に変更できます。代理キーの値は任意であり、自然データとは無関係であるため、キーを変更する必要はありません。
  • 候補キーとして使用できる列がある場合でも、複合主キーとして列の大きなサブセットを使用する必要がある場合があります。キーの格納はかさばり、確かに単一の整数よりもはるかにかさばります。したがって、サロゲートキーを使用すると、ストレージ効率に関して利点があります。
  • 複数列のPKを操作すると、開発者はJOIN句とWHERE句でより長い条件を記述する必要があるため、コーディング作業が増えます。また、要件が変更されて(名前、年齢、住所)が十分なPKでなくなった場合は、PKに4番目の列を追加する必要があります。次に、すべてのアプリケーションのすべてのSQLコードを変更する必要があります。

したがって、代理キーには正当な利点があります。

とはいえ、代理キーは頻繁に使用されすぎます。多くのアプリケーションフレームワーク(Ruby on Railsなど)は、適切かどうかに関係なく、すべてのテーブルに名前が付けられた整数の代理キーを持つデフォルトを使用しますID。テーブルごとにPK列を指定できますが、多くのプログラマーは原則としてデフォルトを採用しているため、意味のないテーブルデザインになります。私が見た中で最悪の例は、すべての多対多のテーブルに不要なID列があることです。

価値のあることとして、代理キーの使用は正規化とは何の関係もありません。つまり、正規化のルールは、代理キーの使用を奨励も阻止もしません。

代理キーをサポートするすべてのデータベースは、現在のセッションで最後に生成されたID値を返す関数も提供します。@JSteadが述べたように、SQLServerではまたは@@IDENTITYですSCOPE_IDENTITY()。MySQLでは、それはLAST_INSERT_ID()です。等々。

これらの関数は単一の値のみを返すため、単一のINSERTステートメントに複数の行を挿入した場合、生成されたすべてのID値を取得することはできません。それは制限です。

于 2012-12-09T18:47:00.800 に答える
2

私はそれが使い古されていることに同意しますが、それはRDBMSの世界に存在します。あなたはデータベースを指定しませんでした、そしてこれはどんな実装よりも哲学的な議論であるため、以下の私の例ではSQLサーバーを使用します。

auto_incrementまたはidentityの最良のケースは、ワイドまたはマルチ列の自然キーである多くの場合、自然キーよりもはるかに効率的で使いやすいことです。

以下の表を見てみましょう。

CREATE TABLE TABLE_OBJECT (
 Table_ID int identity(1,1),
 Server_NME varchar(128),
 Database_NME varchar(128),
 Table_NME varchar(128)
)

CREATE TABLE COLUMN_OBJECT (
 Column_ID int identity(1,1),
 Table_ID int not null,
 Server_NME varchar(128),
 Database_NME varchar(128),
 Table_NME varchar(128),
 Column_NME varchar(128)
)

ここで、このシナリオで2つのテーブルを結合したいとしますが、IDがありません。

select * from TABLE_OBJECT to
inner join COLUMN_OBJECT co on co.Server_NME = to.Server_NME
                          and co.Database_NME = to.Database_NME
                          and co.Table_NME = to.Table_NME

書くのが面倒であることに加えて、1行を比較するために6 *(128)バイトを読み取らなければならなかったのも非常に非効率的です。

次に、それを次の単純さと比較します。

select * from TABLE_OBJECT to
inner join COLUMN_OBJECT co on co.Table_id = to.Table_ID

上記の例では、1つの行を比較するために2 *(4)バイトを読み取るだけで済みました。行が多い場合、これは大きな違いです。

次に、プレーンストレージ側もあります。

CREATE TABLE COLUMN_OBJECT (
     Server_NME varchar(128),
     Database_NME varchar(128),
     Table_NME varchar(128),
     Column_NME varchar(128)
    )

CREATE TABLE COLUMN_OBJECT (
     Column_ID int identity(1,1),
     Table_ID int not null,
     Column_NME varchar(128)
    )

このストレージは、IDバージョン2 *(4)+ 128バイトであるのに対し、自然キーバージョンは4*128バイトです。

table_idとcolumn_nmeに一意性制約を設定することで、一意性を保証することもできます。次に、親テーブルで、table_nme、database_nme、server_nmeに一意の制約を設定します。正直なところ、私はデータベーステーブルを作成した可能性が高く、テーブルにはdatabase_idとtable_nmeに対する一意の制約がありますが、あなたはその考えを理解しています。一意のインデックスに対して適切な列が選択されている場合、一意性が問題になることはありません。

以前に挿入されたIDまたはauto_incremenetの値を取得することは、ほとんどの言語でも簡単です。

select @@IDENTITY
or
select LAST_INSERT_ID()

すべての言語には、最後の言語を取得する方法があります。

于 2012-12-09T15:29:40.873 に答える