Могут ли столбцы таблицы с внешним ключом быть NULL?

У меня есть таблица, которая имеет несколько столбцов ID для других таблиц.

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

(Вероятно, это зависит от сервера базы данных, я использую тип таблицы MySQL и InnoDB)

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

Ответ 1

Да, вы можете принудительно применять ограничение только тогда, когда значение не равно NULL. Это можно легко протестировать в следующем примере:

CREATE DATABASE t;
USE t;

CREATE TABLE parent (id INT NOT NULL,
                     PRIMARY KEY (id)
) ENGINE=INNODB;

CREATE TABLE child (id INT NULL, 
                    parent_id INT NULL,
                    FOREIGN KEY (parent_id) REFERENCES parent(id)
) ENGINE=INNODB;


INSERT INTO child (id, parent_id) VALUES (1, NULL);
-- Query OK, 1 row affected (0.01 sec)


INSERT INTO child (id, parent_id) VALUES (2, 1);

-- ERROR 1452 (23000): Cannot add or update a child row: a foreign key 
-- constraint fails (`t/child`, CONSTRAINT `child_ibfk_1` FOREIGN KEY
-- (`parent_id`) REFERENCES `parent` (`id`))

Первая вставка пройдет, потому что мы вставляем NULL в parent_id. Вторая вставка терпит неудачу из-за ограничения внешнего ключа, поскольку мы попытались вставить значение, которое не существует в таблице parent.

Ответ 2

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

Ответ 3

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

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

Ответ 4

Выше работает, но это не так. Обратите внимание на УДАЛЕННЫЙ КАСКАД

CREATE DATABASE t;
USE t;

CREATE TABLE parent (id INT NOT NULL,
                 PRIMARY KEY (id)
) ENGINE=INNODB;

CREATE TABLE child (id INT NULL, 
                parent_id INT NULL,
                FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE CASCADE

) ENGINE=INNODB;


INSERT INTO child (id, parent_id) VALUES (1, NULL);
-- Query OK, 1 row affected (0.01 sec)

Ответ 5

Другой способ обойти это - вставить элемент DEFAULT в другую таблицу. Например, любая ссылка на uuid = 00000000-0000-0000-0000-000000000000 в другой таблице будет указывать на отсутствие действий. Вам также необходимо установить все значения для этого идентификатора как "нейтральные", например, 0, пустая строка, ноль, чтобы не влиять на логику вашего кода.

Ответ 6

Я тоже застрял в этом вопросе. Но я решил просто, определив внешний ключ как unsigned integer. Найдите ниже example-

CREATE TABLE parent (
   id int(10) UNSIGNED NOT NULL,
    PRIMARY KEY (id)
) ENGINE=INNODB;

CREATE TABLE child (
    id int(10) UNSIGNED NOT NULL,
    parent_id int(10) UNSIGNED DEFAULT NULL,
    FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE CASCADE
) ENGINE=INNODB;

Ответ 7

Да, значение может быть NULL, но вы должны быть явным. Я уже сталкивался с такой же ситуацией и раньше, и легко забыть ПОЧЕМУ это происходит, и поэтому нужно немного вспомнить, что нужно сделать.

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

Но ведь это удовольствие от программирования, не так ли? Создание собственных проблем, а затем их исправление! Ура!