У меня есть таблица, которая имеет несколько столбцов ID для других таблиц.
Мне нужен внешний ключ для обеспечения целостности, только если я помещаю туда данные. Если позднее я выполню обновление, чтобы заполнить этот столбец, он также должен проверить ограничение.
(Вероятно, это зависит от сервера базы данных, я использую тип таблицы MySQL и InnoDB)
Я считаю, что это разумное ожидание, но поправьте меня, если я ошибаюсь.
Да, вы можете принудительно применять ограничение только тогда, когда значение не равно 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
.
Я обнаружил, что при вставке значения нулевого столбца должны были быть объявлены как NULL, иначе я бы получил ошибку нарушения ограничения (в отличие от пустой строки).
Да, это будет работать так, как вы ожидаете. К сожалению, у меня, похоже, не получается найти явное утверждение этого в руководство MySQL.
Внешние ключи означают, что значение должно существовать в другой таблице. NULL относится к отсутствию значения, поэтому, когда вы устанавливаете столбец в 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) ON DELETE CASCADE
) ENGINE=INNODB;
INSERT INTO child (id, parent_id) VALUES (1, NULL);
-- Query OK, 1 row affected (0.01 sec)
Я тоже застрял в этом вопросе. Но я решил просто, определив внешний ключ как 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;
Другой способ обойти это - вставить пустой элемент в другую таблицу. Например, любая ссылка на uuid = 00000000-0000-0000-0000-000000000000 в другой таблице будет указывать на отсутствие действий. Вам также нужно установить все значения для этого идентификатора как "нейтральные", например, 0, пустая строка, ноль, чтобы не влиять на логику кода.