web-dev-qa-db-ja.com

外部キーは一意でないインデックスを参照できますか?

外部キーは単一の行が単一の行を参照する必要があることを意味すると思っていましたが、これが明らかにそうではないいくつかのテーブルを調べています。 Table1にはcolumn1があり、table2のcolumn2に外部キー制約がありますが、table2にはcolumn2に同じ値を持つ多くのレコードがあります。 column2には一意でないインデックスもあります。これは何を意味するのでしょうか?外部キー制約は、正しい列に正しい値を持つ少なくとも1つのレコードが存在する必要があることを単に意味しますか?そのようなレコードが1つだけ存在する必要があることを意味すると思った(nullが画像にどのように適合するかはわかりませんが、現時点ではそれほど心配していません)。

更新:どうやら、この動作はMySQLに固有のものです。これは私が使用していたものですが、元の質問では触れていません。

40
allyourcode

MySQLドキュメント から:

InnoDBでは、外部キー制約が一意でないキーを参照できます。これは、標準SQLのInnoDB拡張です。

ただし、参照されるテーブルの一意でない列で外部キーを使用しないようにする実際的な理由があります。つまり、その場合の「ON DELETE CASCADE」のセマンティクスはどうあるべきでしょうか。

ドキュメントはさらに助言します

非一意キーまたはNULL値を含むキーへの外部キー参照の処理は十分に定義されていません(...)UNIQUE(PRIMARYを含む)およびNOT NULLキーのみを参照する外部キーを使用することをお勧めします。

45
Hobbes

あなたの分析は正しいです。キーは一意である必要はなく、制約は一致する行のセットに作用します。通常は有用な動作ではありませんが、必要な場所で状況が発生する可能性があります。

7
chaos

はい、基本的に任意のテーブルの任意の列への外部キーを作成できます。ただし、ほとんどの場合、それらを主キーに作成します。

主キーを指さない外部キーを使用する場合、パフォーマンスのために参照される列への(一意でない)インデックスを作成することもできます。

使用しているRDBMSによって異なります。暗黙のうちにこれを行う人もいれば、他のトリックを使う人もいます。 RTM。

3
Evan

これが発生した場合、通常は2つの外部キーが相互にリンクされていることを意味します。多くの場合、主キーとしてキーを含むテーブルは、スキーマにもありません。

例:COLLEGESとSTUDENTSの2つのテーブルには、どちらもZIPCODEという列があります。

簡単にチェックすると

SELECT * FROM COLLEGES JOIN STUDENTS ON COLLEGES.ZIPCODE = STUDENTS.ZIPCODE

私たちは、その関係が多対多であることを発見するかもしれません。スキーマにZIPCODESというテーブルがあり、主キーがZIPCODEである場合、実際に何が起こっているかは明らかです。

しかし、私たちのスキーマにはそのようなテーブルはありません。ただし、スキーマにそのようなテーブルがないからといって、そのようなデータが存在しないというわけではありません。どこかに、USPOの土地で、ちょうどそのようなテーブルがあります。また、COLLEGES.ZIPCODEとSTUDENTS.ZIPCODEの両方は、たとえそれを認めなくても、そのテーブルへの参照です。

これは、データベース構築の実践よりもデータの哲学と関係がありますが、データには、私たちが発見した特性だけでなく、発見した特性だけが含まれているという基本的なことをきちんと示しています。もちろん、私たちが発見するのは、他の誰かが発明したものかもしれません。それは確かに郵便番号の場合です。

3
Walter Mitty

PostgreSQLもこれを拒否します(とにかく、可能であっても、それが良いアイデアであるとは限りません):

essais=> CREATE TABLE Cities (name TEXT, country TEXT);
CREATE TABLE
essais=> INSERT INTO Cities VALUES ('Syracuse', 'USA');
INSERT 0 1
essais=> INSERT INTO Cities VALUES ('Syracuse', 'Greece');
INSERT 0 1
essais=> INSERT INTO Cities VALUES ('Paris', 'France');
INSERT 0 1
essais=> INSERT INTO Cities VALUES ('Aramits', 'France');
INSERT 0 1
essais=> INSERT INTO Cities VALUES ('Paris', 'USA');
INSERT 0 1

essais=> CREATE TABLE People (name TEXT, city TEXT REFERENCES Cities(name));
ERROR:  there is no unique constraint matching given keys for referenced table "cities"
1
bortzmeyer

ネクロマンシング。
すでに述べたように、一意でないキーを外部キーとして参照しないでください。
しかし、代わりに(削除カスケードの危険なしで)できることは、(少なくともMS-SQLでは)チェック制約を追加することです。
これは外部キーとまったく同じではありませんが、少なくとも無効/孤立した/無効なデータの挿入を防ぎます。

こちらを参照してください(MS-SQLコードをMySQL構文に移植する必要があります)。
非主キーへの外部キー

編集:
Mysql CHECK Constraint に従って、投票の理由を検索していますが、MySQLは実際にはCHECK制約をサポートしていません。
互換性の理由でDDLクエリで定義できますが、無視されるだけです...

しかし、そこで言及されているように、BEFORE INSERTおよびBEFORE UPDATEトリガー。データの要件が満たされていない場合にエラーをスローします。これは基本的には同じですが、さらに大きな混乱を引き起こします。

質問について:

外部キーは単一の行が単一の行を参照する必要があることを意味すると思っていましたが、これが明らかにそうではないいくつかのテーブルを調べています。

正気なRDBMSの場合、これは真実です。
これがMySQLで可能であるという事実は、もう1つの理由です
MySQLは非常識なRDBMSです。
それは高速かもしれませんが、速度の祭壇で参照整合性とデータ品質を犠牲にすることは、品質-rdbmsの私の考えではありません。
実際、ACIDに準拠していない場合、実際には(正しく機能している)RDBMSではありません。

1
Stefan Steiger

どのデータベースについて話しているのですか? SQL 2005では、一意の制約(主キーなど)を持たない列を参照する外部キー制約を作成できません。

create table t1
(
  id int identity,
  fk int
);

create table t2
(
  id int identity,
);

CREATE NONCLUSTERED INDEX [IX_t2] ON [t2] 
(
    [id] ASC
);
ALTER TABLE t1 with NOCHECK
ADD CONSTRAINT FK_t2 FOREIGN KEY (fk)
    REFERENCES t2 (id) ;


Msg 1776, Level 16, State 0, Line 1
There are no primary or candidate keys in the referenced table 't2' 
that match the referencing column list in the foreign key 'FK_t2'.
Msg 1750, Level 16, State 0, Line 1
Could not create constraint. See previous errors.

これを実際に行うことができれば、多対多の関係を効果的に実現できます。これは、中間テーブルなしでは不可能です。これについてもっと聞きたいです...

この関連質問 と回答もご覧ください。

0
cdonner