2017-04-17 115 views
1

這是SQLite的版本3.16.0 2016年11月4日SQLite觸發器奇怪的是失敗的唯一約束

這裏是我的測試模式:

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; 

我可以看到一個新的行表C(它獲取每次我摸到B時間更新):

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

您正在使用REPLACE command,這

對於INSERT命令 「INSERT OR REPLACE」 變種的別名。

trigger documentation說:

ON CONFLICT一個條款可被規定爲觸發器的主體內的UPDATEINSERT動作的一部分。但是,如果將ON CONFLICT子句指定爲引發觸發器的語句的一部分,則會使用外部語句的衝突處理策略。

因此,當外層命令是UPDATE時,您的REPLACE也會變成UPDATE。

作爲解決方法,請將REPLACE替換爲適當的DELETE和INSERT命令。

+0

在我看來,「導致觸發器觸發的語句」是「UPDATE A SET a_id = 2 ...;」,它沒有指定一個「ON CONFLICT」子句,所以在技術上這不適用。 儘管我發現這些doc語句容易解釋,但我認爲,你是對的,因爲'delete' +'insert'確實是一個可行的解決方法。 謝謝! 「 – Knuckles

+0

」默認的衝突解決算法是ABORT。「 –

+0

沒關係。否則,觸發器將同樣失敗 UPDATE B SET descr =「test1」WHERE b_id = 1;',而不是。 – Knuckles