ВСТАВИТЬ ИЛИ ЗАМЕНИТЬ + внешний ключ ВКЛ. УДАЛИТЬ КАСКАД, работающий слишком хорошо

В настоящее время я пытаюсь создать базу данных sqlite, где я могу импортировать таблицу из другой базы данных sqlite (не могу присоединяться) и добавлять дополнительные данные в каждый столбец.

Поскольку нет INSERT OR UPDATE, я придумал это:
Я думал о разделении данных на две таблицы и присоединяюсь к ним после этого, поэтому я могу просто сбросить весь импорт в одну таблицу, заменяя все, что изменилось и управляет дополнительными данными отдельно, поскольку это не изменяется при импорте.

Первая таблица (пусть называется base_data) будет выглядеть как

local_id | remote_id | base_data1 | base_data2 | ...
---------+-----------+------------+------------+----

кроме local_id все будет просто зеркалом удаленной базы данных (я, вероятно, добавлю синхронизируемую метку времени, но это не имеет значения сейчас).

Вторая таблица будет выглядеть аналогично, но remote_id устанавливается как внешний ключ

remote_id | extra_data1 | extra_data2 | ...
----------+-------------+-------------+----

   CREATE TABLE extra_data (
       remote_id INTEGER 
           REFERENCES base_data(remote_id)
           ON DELETE CASCADE ON UPDATE CASCADE
           DEFERRABLE INITIALLY DEFERRED,
       extra_data1 TEXT,
       extra_data2 TEXT,
       /* etc */
   )

Теперь моя идея состояла в просто значении INSERT OR REPLACE INTO base_data ..., потому что в базе данных, в которой я импортирую, нет синхронизации или вообще ничего, и мне пришлось бы сравнить все, чтобы узнать, какая строка у меня есть UPDATE/what to INSERT.

Но здесь проблема: INSERT OR REPLACE на самом деле является DELETE, за которой следует INSERT, а часть удаления запускает внешний ключ ON DELETE, который, как я думал, я мог бы предотвратить, создав ограничение DEFERRED. Это не работает, если я заключу INSERT OR REPLACE в транзакцию. Он всегда удаляет мои дополнительные данные, хотя такой же внешний ключ существует после инструкции.

Можно ли остановить ON DELETE для запуска до завершения INSERT OR REPLACE? Может быть, какой-то специальный режим транзакции/прагма?

Ответ 1

Кажется, что если я заменил часть ON DELETE CASCADE на триггер, например:

CREATE TRIGGER on_delete_trigger
   AFTER DELETE ON base_data
   BEGIN
       DELETE FROM extra_data WHERE extra_data.remote_id=OLD.remote_id;
   END;

Этот триггер запускается только оператором DELETE и должен решить мою проблему до сих пор.

(Ответ, предоставленный OP в вопросе)

Дополнительная информация jmathew, ссылаясь на документация:

Когда стратегия разрешения конфликта REPLACE удаляет строки, чтобы удовлетворить ограничение, удалять триггеры можно только тогда, когда включены рекурсивные триггеры.

Ответ 2

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

Просто отключите проверку внешнего ключа, запустите запрос замены, затем снова включите внешние ключи.

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

Код SQLlite выглядит примерно так:

PRAGMA foreign_keys=OFF;
INSERT OR REPLACE ...;
PRAGMA foreign_keys=ON;