«Сбой ограничения FOREIGN KEY» при обновлении зависимых таблиц во время транзакции - PullRequest
2 голосов
/ 27 сентября 2019

У меня есть три таблицы с такими цепочечными зависимостями:

pragma foreign_keys = ON;
create table foo (id integer primary key);
create table bar (id integer primary key references foo(id));
create table baz (id integer primary key references bar(id));
insert into foo values (1), (2);
insert into bar values (1);
insert into baz values (1);

Я хочу обновить дочерние таблицы bar и baz.Однако при их обновлении в процессе перехода появляется сообщение об ошибке:

begin;
update bar set id = 2 where id = 1;
update baz set id = 2 where id = 1;
commit;
-- Error: FOREIGN KEY constraint failed

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

Ответы [ 3 ]

1 голос
/ 27 сентября 2019

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

create table foo (id integer primary key);
create table bar (id integer primary key references foo(id) on update cascade);
create table baz (id integer primary key references bar(id) on update cascade);

Тогда вам просто нужно обновить bar.id следующим образом, и изменение произойдет автоматически в baz.id.

begin;
update bar set id = 2 where id = 1;
commit;
1 голос
/ 27 сентября 2019

Иногда мне хотелось бы более внимательно прочитать документы .

Каждое ограничение внешнего ключа в SQLite классифицируется как немедленное или отложенное. Ограничения по внешнему ключу являются немедленными по умолчанию. (...)

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

Таким образом, таблица может объявить внешний ключ как DEFERRABLE INITIALLY DEFERRED, чтобы разрешить такой вид обновления.

pragma foreign_keys = ON;
create table foo (id integer primary key);
create table bar (id integer primary key references foo(id) deferrable initially deferred);
create table baz (id integer primary key references bar(id) deferrable initially deferred);
insert into foo values (1), (2);
insert into bar values (1);
insert into baz values (1);

begin;
update bar set id = 2 where id = 1;
update baz set id = 2 where id = 1;
commit;

Но поскольку sqlite не позволяет легко изменять таблицу, приятно знать, что такое поведение возможно получить для существующих таблиц, которые имеют непосредственные внешние ключи с прагмой defer_foreign_keys:

pragma foreign_keys = ON;
create table foo (id integer primary key);
create table bar (id integer primary key references foo(id));
create table baz (id integer primary key references bar(id));
insert into foo values (1), (2);
insert into bar values (1);
insert into baz values (1);

pragma defer_foreign_keys=ON;
begin;
update bar set id = 2 where id = 1;
update baz set id = 2 where id = 1;
commit;

Должен сказать, я не понимаю, как использовать непосредственные внешние ключи, и я не понимаю, почему они должны использоваться по умолчанию - разве что по соображениям производительности?

0 голосов
/ 27 сентября 2019

Вы, вероятно, пытаетесь избежать этого ... но ...

begin;
delete baz where id = 1;
delete bar where id = 1;
insert into bar values (2);
insert into baz values (2);
commit;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...