【问题标题】:"FOREIGN KEY constraint failed" when updating dependent tables during a transaction在事务期间更新依赖表时出现“外键约束失败”
【发布时间】:2019-09-27 16:44:56
【问题描述】:

我有三个表具有这样的链依赖关系:

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);

我想更新子表barbaz。但是,在事务中更新它们时,我收到一个错误:

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

如何同时更新子表以避免外键约束错误?

【问题讨论】:

    标签: sql sqlite


    【解决方案1】:

    如果您在每个外键上设置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;
    

    【讨论】:

      【解决方案2】:

      有时我希望我能更仔细地阅读docs

      SQLite 中的每个外键约束都被分类为立即或延迟。 外键约束默认是即时的。(...)

      如果一个语句修改了数据库的内容,使得一个立即外键约束在该语句的结尾违反了,一个异常被抛出并且效果如下:声明被还原。相反,如果一条语句修改了数据库的内容,从而违反了延迟外键约束,则不会立即报告该违反。在事务尝试提交之前,不会检查延迟的外键约束。

      因此,表可以将外键声明为 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 获得此行为:

      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;
      

      我必须说,我不明白直接外键的使用,我不明白为什么它们应该是默认值——除非出于性能原因?

      【讨论】:

        【解决方案3】:

        您可能试图避免这种情况...但是...

        begin;
        delete baz where id = 1;
        delete bar where id = 1;
        insert into bar values (2);
        insert into baz values (2);
        commit;
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-05-29
          • 1970-01-01
          • 1970-01-01
          • 2014-10-04
          相关资源
          最近更新 更多