Может ли внешний ключ ссылаться на неуникальный индекс? - PullRequest
38 голосов
/ 26 февраля 2009

Я думал, что внешний ключ означает, что одна строка должна ссылаться на одну строку, но я смотрю на некоторые таблицы, где это определенно не так. Таблица 1 имеет столбец 1 с ограничением внешнего ключа для столбца 2 в таблице 2, НО в таблице 2 есть много записей с одинаковым значением в столбце 2. Также есть неуникальный индекс для column2. Что это значит? Означает ли ограничение внешнего ключа просто, что должна существовать хотя бы одна запись с правильными значениями в правильных столбцах? Я подумал, что это означает, что должна быть ровно одна такая запись (не знаю, как нули вписываются в картинку, но сейчас меня это меньше беспокоит).

обновление: по-видимому, это поведение специфично для MySQL, который я использовал, но я не упомянул об этом в своем первоначальном вопросе.

Ответы [ 7 ]

44 голосов
/ 01 февраля 2010

С MySQL документация :

InnoDB позволяет ограничению внешнего ключа ссылаться на неуникальный ключ. Это расширение InnoDB для стандартного SQL.

Однако существует практическая причина избегать внешних ключей в неуникальных столбцах ссылочной таблицы. То есть, что в этом случае должно быть семантикой «ON DELETE CASCADE»?

В документации содержатся дополнительные рекомендации :

Обработка ссылок на внешние ключи на неуникальные ключи или ключи, которые содержат значения NULL, не очень хорошо определена (...) Рекомендуется использовать внешние ключи, которые ссылаются только на уникальные (включая ПЕРВИЧНЫЕ) и НЕ НУЛЬНЫЕ ключи.

7 голосов
/ 26 февраля 2009

Ваш анализ верен; ключи не должны быть уникальными, и ограничения будут действовать на множество совпадающих строк. Обычно это бесполезное поведение, но могут возникнуть ситуации, когда вы этого хотите.

3 голосов
/ 26 февраля 2009

Когда это происходит, это обычно означает, что два внешних ключа связаны друг с другом. Часто таблица, которая будет содержать ключ в качестве первичного ключа, отсутствует даже в схеме.

Пример: две таблицы, COLLEGES и STUDENTS, обе содержат столбец с именем ZIPCODE.

Если мы сделаем быструю проверку

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

Мы могли бы обнаружить, что отношения многие ко многим. Если бы в нашей схеме была таблица ZIPCODES с первичным ключом ZIPCODE, было бы очевидно, что происходит на самом деле.

Но наша схема не имеет такой таблицы. Однако то, что наша схема не имеет такой таблицы, не означает, что таких данных не существует. где-то на земле USPO есть такой стол. И COLLEGES.ZIPCODE, и STUDENTS.ZIPCODE являются ссылками на эту таблицу, даже если мы ее не признаем.

Это больше связано с философией данных, чем с практикой построения баз данных, но оно четко иллюстрирует нечто фундаментальное: данные имеют обнаруживаемые нами характеристики, а не только характеристики, которые мы изобретаем. Конечно, то, что мы обнаруживаем, может быть изобретено кем-то другим. Это, безусловно, имеет место с ZIPCODE.

3 голосов
/ 26 февраля 2009

Да, вы можете создавать внешние ключи практически для любых столбцов в любой таблице. Однако чаще всего вы создаете их в первичном ключе.

Если вы используете внешние ключи, которые не указывают на первичный ключ, вы также можете создать (неуникальный) индекс для столбцов, на которые ссылаются ради производительности.

Зависит от используемой СУБД. Я думаю, что некоторые делают это для вас неявно, или используют некоторые другие приемы. RTM.

1 голос
/ 26 февраля 2009

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"
0 голосов
/ 17 октября 2014

Necromancing.
Как уже говорили другие, вы не должны ссылаться на неуникальный ключ как на внешний ключ.
Но то, что вы можете сделать вместо этого (без опасности удаления каскада), это добавить проверочное ограничение (по крайней мере, в MS-SQL).
Это не совсем то же самое, что внешний ключ, но, по крайней мере, он предотвратит вставку неверных / осиротевших / мертвых данных.

См. Здесь для справки (вам придется перенести код MS-SQL на синтаксис MySQL):
Внешний ключ к не первичному ключу

Редактировать:
В поисках причин понижения, согласно Mysql CHECK Constraint , MySQL на самом деле не поддерживает ограничения CHECK.
Вы можете определить их в своем запросе DDL по причинам совместимости, но они просто игнорируются ...

Но, как уже упоминалось, вы можете создать триггеры BEFORE INSERT и BEFORE UPDATE, которые будут выдавать ошибку, если требования к данным не будут выполнены, что в основном то же самое, за исключением того, что это еще больший беспорядок .

По вопросу:

Я думал, что внешний ключ означает, что одна строка должна ссылаться на одна строка, но я смотрю на некоторые таблицы, где это определенно не тот случай.

В любой здравомыслящей РСУБД это действительно так.
Тот факт, что это возможно в MySQL, является еще одной причиной, почему
MySQL - это встроенная СУБД .
Это может быть быстро, но жертвовать ссылочной целостностью и качеством данных на алтаре скорости - это не моя идея качества rdbms.
На самом деле, если он не совместим с ACID, это не совсем (правильно функционирующая) СУБД.

0 голосов
/ 26 февраля 2009

О какой базе данных мы говорим? В 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.

Если бы вы могли на самом деле сделать это, вы бы фактически имели отношение многие ко многим, что невозможно без промежуточной таблицы. Мне было бы действительно интересно услышать больше об этом ...

См. этот связанный вопрос и ответы на него.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...