В SQL, будет ли работать внешний ключ (FOREIGN KEY) на составной уникальный ключ (UNIQUE KEY), если одна из колонок в уникальном ключе содержит NULL?

Ответ

Ответ зависит от СУБД. В большинстве систем, включая MySQL, это работает, но с важным ограничением.

Поведение в MySQL/InnoDB:

  • Составной UNIQUE KEY допускает дублирование строк, если хотя бы один столбец в ключе содержит NULL.
  • FOREIGN KEY может ссылаться на такую строку, но целостность данных может быть нарушена, так как в родительской таблице может быть несколько "неуникальных" строк с NULL.

Пример в MySQL:

CREATE TABLE Parent (
    col1 INT,
    col2 INT,
    UNIQUE KEY uk_parent (col1, col2) -- Составной уникальный ключ
);

CREATE TABLE Child (
    id INT PRIMARY KEY,
    fk_col1 INT,
    fk_col2 INT,
    FOREIGN KEY (fk_col1, fk_col2)
        REFERENCES Parent(col1, col2)
);

-- Вставка в Parent (допустима, т.к. col2 = NULL)
INSERT INTO Parent VALUES (1, NULL);
INSERT INTO Parent VALUES (1, NULL); -- Повторная вставка разрешена!

-- Вставка в Child (будет работать)
INSERT INTO Child VALUES (100, 1, NULL);

Почему это проблема: Внешний ключ теперь ссылается на одну из нескольких возможных строк в Parent, что нарушает принцип однозначной ссылки. В PostgreSQL поведение строже: UNIQUE ограничение считает все NULL значения разными, поэтому дублирования не возникнет.

Ответ 18+ 🔞

А, ну это классика, блядь! Сейчас разберёмся, как эта конструкция на самом деле работает, а то народ думает, что раз UNIQUE написал — значит, всё уникально, как хуй с горы. Ан нет, ёпта!

Смотри, в MySQL, а конкретно в InnoDB, эта штука работает, но с таким подвохом, что можно охуеть. Составной UNIQUE KEY — он как бы уникальный, но только до тех пор, пока в нём не появляется NULL. А как появляется — всё, пиши пропало, уникальность накрывается медным тазом.

Вот в чём, блядь, фокус: Если хоть один столбец в этом ключе — NULL, то всю строку система считает "неопределённой", и её можно дублировать до овердохуища. Представь, будто ты пытаешься найти уникального человека по имени и фамилии, а у него в паспорте на месте фамилии прочерк. И таких "прочерчных" Иванов — целая толпа, блядь! Какой из них твой? Хуй его знает!

И вот эта самая FOREIGN KEY из дочерней таблицы спокойно ссылается на такую строку в родительской. Но ссылается-то она на кого? На одного из этих клонов-призраков! Целостность данных, которую ты хотел сохранить, превращается в пиздопроебибну. Одна ссылка — а родителей несколько. Это как найти в роддоме трёх женщин, которые все утверждают, что они мать твоего ребёнка. Волнение ебать, да?

Смотри, как это выглядит в коде, тут всё честно:

CREATE TABLE Parent (
    col1 INT,
    col2 INT,
    UNIQUE KEY uk_parent (col1, col2) -- Ключ-то есть, но...
);

CREATE TABLE Child (
    id INT PRIMARY KEY,
    fk_col1 INT,
    fk_col2 INT,
    FOREIGN KEY (fk_col1, fk_col2)
        REFERENCES Parent(col1, col2)
);

-- Вставляем в родителя
INSERT INTO Parent VALUES (1, NULL); -- Нормально
INSERT INTO Parent VALUES (1, NULL); -- И ещё раз нормально! Уже две одинаковых строки, блядь!

-- А теперь дитя пытается найти папу
INSERT INTO Child VALUES (100, 1, NULL); -- Вставится! Но на какого из двух пап ссылается? На любого!

Вот и получается, что внешний ключ вроде бы есть, а однозначности — нихуя. Ребёнок тычется в одного из двух, а то и десяти, одинаковых папаш. Пиздец, да?

А в PostgreSQL, например, народ поумнее будет. Там UNIQUE считает все NULL значения РАЗНЫМИ, поэтому второй (1, NULL) уже не прокатит. Там хоть какая-то определённость, блядь.

Так что, чувак, если используешь MySQL и NULL в уникальных ключах — подозрение ебать чувствую. Либо запрещай NULL в этих колонках (NOT NULL ставь), либо готовься к тому, что твоя ссылочная целостность будет иметь доверия ебать ноль. В рот меня чих-пых, вот такие дела!