Почему составные внешние ключи требуют отдельного уникального ограничения?

10

Вот простая таблица, где записи могут ссылаться на родительские записи в той же таблице:

CREATE TABLE foo (
    id         SERIAL  PRIMARY KEY,
    parent_id  INT     NULL,
    num        INT     NOT NULL,
    txt        TEXT    NULL,
    FOREIGN KEY (parent_id) REFERENCES foo(id)
);

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

    FOREIGN KEY (parent_id, num) REFERENCES foo(id, num)

и получил ОШИБКУ: не существует уникального ограничения, соответствующего данным ключам для ссылочной таблицы "foo" .

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

Zilk
источник

Ответы:

11

Это ограничение СУБД - во всех, насколько я знаю. И не только при добавлении столбца, но и при перестановке столбцов. Если у нас есть UNIQUEограничение на (a1, a2), мы не можем добавить это FOREIGN KEY, REFERENCES (a2, a1)если нет уникального ограничения на то, (a2, a1)что по существу является избыточным.

Не было бы ужасно трудно добавить это как особенность:

При наличии UNIQUEограничения (a)любая (a, b, c, ..., z)или (b,c, ...a, ...z)комбинация также гарантируется UNIQUE.

или обобщение:

При наличии UNIQUEограничения (a1, a2, ..., aN)любая (a1, a2, ..., aN, b1, b2, ..., bM)комбинация или любая перестановка также гарантируется UNIQUE.

Похоже, что его не спрашивали или не считали достаточно высоким приоритетом для реализации.

Вы всегда можете сделать запрос - в соответствующем канале - для реализации функции. Или даже реализовать это самостоятельно, если СУБД с открытым исходным кодом, как Postgres.

ypercubeᵀᴹ
источник
Я не уверен, что это было бы так просто .. Как насчет частичных индексов или значений NULL? и т. д. NULL может по-прежнему работать нормально, если вы довольны NULL != NULL. Во всяком случае .. :)
Joishi Bodio
@JoishiBodio Я не думаю, что нули - это проблема. УНИКАЛЬНЫЕ ограничения могут быть определены или обнуляемые столбцы, а также. По умолчанию, если какой-либо столбец имеет значение NULL, ограничение передается и строка принимается.
ypercubeᵀᴹ
Во-вторых, если a1, a2, ... aN не обнуляются и b1, b2, bM - мы можем столкнуться с проблемами. Но эта функция наверняка может быть реализована для необнуляемых столбцов. Что, вероятно, вызывает беспокойство, это последствия для эффективности.
ypercubeᵀᴹ
Я знаком с тем, UNIQUE INDEXгде находятся колонки NULLABLE... вот почему я упомянул это. :) Но я согласен - в случае отсутствия NULL (и не частичного индекса) это, вероятно, довольно просто.
Джоиши Бодио
5

Внешние ключи в общем (не только составные) ДОЛЖНЫ указывать на УНИКАЛЬНЫЙ КЛЮЧ некоторого вида в другой таблице. Если бы они этого не сделали, не было бы никакой целостности реляционных данных.

Это вызывает недовольство, потому что, хотя у вас есть уникальный ключ на (id) ... у вас нет уникального ключа на (id, num) .. Таким образом, что касается БД, пара (id, num) НЕ ГАРАНТИРУЕТСЯ быть уникальным. Мы, как люди, можем понять, что это будет уникально, но я уверен, что было бы много дополнительного кода, который они должны были бы добавить, чтобы сделать Postgres достаточно умным, чтобы увидеть, что "о, эй ... id должен быть уникальным , id, num также должен быть уникальным "..

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

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

Джоиши Бодио
источник