尽管使用了触发器,但复合主键上的“唯一约束失败”

`UNIQUE constraint failed` on composite primary key despite use of trigger

考虑到我们在尝试 插入 新行到 table 时遇到 UNIQUE constraint failed 错误。

该行由几个主键组成,这些主键链接在一起以创建一个复合键约束,即由 4 个字段idid_xid_yid_z

第一个 id 键需要是 unique 键,即 "auto-incremented" 而 id_xid_yid_zforeign keys.

由于我们无法在 sqlite 中的 复合键 上使用 自动递增 功能,因此我们求助于使用以下触发器我们在其中查找每个字段的最高整数并加 1 以满足 uniqueness 约束:

CREATE TRIGGER [autoincrement]
         AFTER INSERT
            ON table_main
          WHEN NEW.id IS NULL
BEGIN
    UPDATE table_main
        SET id = IFNULL((SELECT MAX(id) FROM table_main) + 1, 0),
        id_x = IFNULL((SELECT MAX(id_x) FROM table_main) + 1, 0),
        id_y = IFNULL((SELECT MAX(id_y) FROM table_main) + 1, 0),
        id_z = IFNULL((SELECT MAX(id_z) FROM table_main) + 1, 0);
END; 

尽管如此,我们仍然遇到 UNIQUE constraint failed 错误。

更新:id_z 有一个 forign key 约束。

Since we cannot use the auto-increment feature on a composite key

下面有一个复合键,其唯一 ID 递增(您称之为自动递增):-

DROP TABLE IF EXISTS table_main;
DROP TABLE IF EXISTS table_fkx;
DROP TABLE IF EXISTS table_fky;
DROP TABLE IF EXISTS table_fkz;
DROP TRIGGER IF EXISTS [autoincrement];
CREATE TABLE IF NOT EXISTS table_fkx (id INTEGER PRIMARY KEY, datacol TEXT);
CREATE TABLE IF NOT EXISTS table_fky (id INTEGER PRIMARY KEY, datacol TEXT);
CREATE TABLE IF NOT EXISTS table_fkz (id INTEGER PRIMARY KEY, datacol TEXT);
CREATE TABLE IF NOT EXISTS table_main (
    id INTEGER PRIMARY KEY, 
  id_x INTEGER REFERENCES table_fkx(id), 
    id_y INTEGER REFERENCES table_fky(id), 
    id_z INTEGER REFERENCES table_fkz(id), 
    UNIQUE(id, id_x, id_y, id_z)
);
/*
CREATE TRIGGER [autoincrement]
         AFTER INSERT
            ON table_main
          WHEN NEW.id IS NULL
BEGIN
    UPDATE table_main
        SET id = IFNULL((SELECT MAX(id) FROM table_main) + 1, 0),
        id_x = IFNULL((SELECT MAX(id_x) FROM table_main) + 1, 0),
        id_y = IFNULL((SELECT MAX(id_y) FROM table_main) + 1, 0),
        id_z = IFNULL((SELECT MAX(id_z) FROM table_main) + 1, 0);
END;
*/

INSERT INTO table_fkx VALUES (10,'some data'),(33,'more data'),(56,'even more data');
INSERT INTO table_fky VALUES (73,'some data'),(1200,'more data'),(560,'even more data');
INSERT INTO table_fkz VALUES (15,'some data'),(1500,'more data'),(123456,'even more data');
INSERT INTO table_main (id_x,id_y,id_z) VALUES 
    (10,1200,15),(56,1200,15),(33,560,15),(10,73,15) -- etc
;
-- INSERT what could be a considered a duplicate but now is not as the autoincremnt(sic) makes it unique
INSERT INTO table_main (id_x,id_y,id_z) VALUES (33,560,15); -- i.e. same as 3rd
SELECT * FROM table_main 
    JOIN table_fkx ON id_x = table_fkx.id 
    JOIN table_fky ON id_y = table_fky.id
    JOIN table_fkz ON id_z = table_fkz.id
;

但是,如果复合键的一部分不存在,则意味着您可以有效地插入引用相同外键的潜在无用行。

也许不是尽管触发,而是因为触发。

这会将 table_main 中的 所有 行更新为相同的键(当 NEW.id 为空时)。当 table_main 中有 2(或更多)行时,这肯定会导致违反约束。

UPDATE table_main
        SET id = IFNULL((SELECT MAX(id) FROM table_main) + 1, 0),
        id_x = IFNULL((SELECT MAX(id_x) FROM table_main) + 1, 0),
        id_y = IFNULL((SELECT MAX(id_y) FROM table_main) + 1, 0),
        id_z = IFNULL((SELECT MAX(id_z) FROM table_main) + 1, 0);

如果NEW.id不为空,则问题出在其他地方。