Я использую MySQL. Предположим, что у меня есть hierarchy
таблицы с двумя столбцами: id
, parent_id
.
parent_id
ссылается на id
другой строки той же таблицы, поэтому у меня есть внешний ключ.
В таблице hierarchy
содержатся некоторые данные, но теперь они не актуальны.
У меня также есть вторая таблица с именем new_hierarchy_entries
которая имеет одинаковые столбцы, но нет ограничений внешнего ключа.
new_hierarchy_entries
содержит:
id parent_id
2 1
1 null
Теперь я хочу скопировать все строки из new_hierarchy_entries
в hierarchy
. Когда я бегу наивно:
INSERT INTO hierarchy SELECT * FROM new_hierarchy_entries
Я получаю сообщение об ошибке: Не удается добавить или обновить дочернюю строку: ограничение внешнего ключа завершается с ошибкой (my_db. hierarchy
, CONSTRAINT hierarchy_ibfk_2 ИНОСТРАННЫЙ КЛЮЧ (parent_id) hierarchy
ССЫЛКИ (id))
Конечно, если строки вставлены один за другим, первая строка (id = 2, parent = 1) не может быть вставлена, потому что в hierarchy
таблицы нет строки с id = 1.
С другой стороны, если бы все строки были добавлены сразу, то ограничения были бы выполнены. Итак, как я могу скопировать строки таким образом, что я уверен, что ограничения выполняются после копирования, но они могут не выполняться при копировании?
Сортировка строк new_hierarchy_entries
по id
не поможет. Я не могу предположить, что parent_id < id
в той же строке.
Сортировка строк new_hierarchy_entries
по иерархии (используя терминологию дерева, дайте мне сначала, потом их родители и т.д.), Но я не уверен, как это сделать в запросе MySQL.
Я играл с идеей временно отключить FOREIGN_KEY_CHECKS. Но потом я мог вставить непоследовательные данные, и я бы не узнал. Превращение FOREIGN_KEY_CHECKS не делает проверку базы данных согласованностью всех данных. В любом случае, потребуется слишком много ресурсов.
Это сложно. Я не знаю, как заставить MySQL повторно проверять ссылки на внешние ключи после включения FOREIGN_KEY_CHECKS.
Вы можете проверить себя на сиротские строки, а если есть, откиньтесь назад.
BEGIN;
SET SESSION FOREIGN_KEY_CHECKS=0;
INSERT INTO hierarchy SELECT * FROM new_hierarchy_entries;
SET SESSION FOREIGN_KEY_CHECKS=1;
SELECT COUNT(*) FROM hierarchy AS c
LEFT OUTER JOIN hierarchy AS p ON p.id=c.parent_id
WHERE p.id IS NULL;
-- if count == 0 then...
COMMIT;
-- otherwise ROLLBACK and investigate the bad data
Еще одна возможность - использовать INSERT с опцией IGNORE, которая пропустит неудачные строки. Затем повторите ту же инструкцию в цикле, если вы видите, что "затронутые строки" больше 0.
INSERT IGNORE INTO hierarchy SELECT * FROM new_hierarchy_entries;
INSERT IGNORE INTO hierarchy SELECT * FROM new_hierarchy_entries;
INSERT IGNORE INTO hierarchy SELECT * FROM new_hierarchy_entries;
...