Sqlite触发器奇怪地在唯一约束上失败

时间:2017-04-17 22:19:30

标签: triggers sqlite

这是SQLite版本3.16.0 2016-11-04

这是我的测试架构:

PRAGMA foreign_keys = ON;
CREATE TABLE A (a_id integer primary key, x int);
CREATE TABLE B (b_id integer primary key, a_id integer not null references A(a_id) ON DELETE RESTRICT ON UPDATE CASCADE, descr text);
CREATE TABLE C (id integer primary key, dt DATETIME DEFAULT CURRENT_TIMESTAMP);
CREATE TRIGGER Tb after update on B begin
    replace into C (id) select NEW.b_id;
end;

这里有一些测试数据(根据.dump):

INSERT INTO "A" VALUES(1, 5000);
INSERT INTO "B" VALUES(1, 1, 'none');

现在,如果我手动更新表B中的行,就像这样:

UPDATE B SET descr = "test1" WHERE b_id = 1;

我可以在表B中看到一个新行(每当我触摸C时都会更新):

1|2017-04-17 21:59:42

我也可以手动"触摸" C与触发器完成的方式相同:

replace into C (id) select 1;

正如预期的那样,dt列会更新。到目前为止都很好。 但突然间,如果我更改a_id,则引用B

UPDATE A SET a_id = 2 WHERE a_id = 1;

我得到Error: UNIQUE constraint failed: C.id

根据我的理解,从B.a_idA.a_id的外键会更新B的行。这导致触发器试图触及C。然后出于某种原因失败了,尽管它在之前的场景中运行得非常好。

为什么会发生这种情况,我该如何解决?

1 个答案:

答案 0 :(得分:1)

您使用的是REPLACE command

  

是" INSERT OR REPLACE"的别名。 INSERT命令的变体。

trigger documentation说:

  

ON CONFLICT子句可以指定为触发器正文中UPDATEINSERT操作的一部分。但是,如果将ON CONFLICT子句指定为导致触发器触发的语句的一部分,则使用外部语句的冲突处理策略。

因此,当外部命令是UPDATE时,您的REPLACE也会变为UPDATE。

作为解决方法,请使用适当的DELETE和INSERT命令替换REPLACE。