如何创建一个即使列为NULL也能工作的UNIQUE约束

时间:2014-07-17 00:32:57

标签: sql sqlite

我有一个表,我希望防止插入重复的行。

CREATE TABLE blah(id INTEGER PRIMARY KEY ASC, a INTEGER, b INTEGER, c, d, e, f,
    UNIQUE (a,b,c,d,e,f) ON CONFLICT IGNORE);

现在我插入几行:

INSERT INTO event2 (a,b,c,d) 
    VALUES (1,1405451500,"Y",100), 
           (1,1405451555,"Z",115);

现在,如果我重新运行上面的命令,我希望它不会插入任何内容,因为新行完全匹配已经存在的行。相反,我看到插入了两个新行。

读取SQLite documentation,如果一个或多个列为NULL,则SQLite似乎不会强制执行UNIQUE约束。如果有某种方法可以使用其他一些机制来实现这一目标?在上述情况下,cdef列可以为null。或者我可以使用空字符串而不是NULL但是阅读其他一些答案似乎表明null不应该与空字符串混淆,因为它们表示单独的东西。

1 个答案:

答案 0 :(得分:0)

您可以使用BEFORE INSERT触发器来检查数据的正确性。

请注意,这会在每次插入之前进行全表扫描,因此对于较大的表,它会导致插入速度变慢!

要加快速度(并避免全表扫描),您应CREATE INDEX,其中包括触发器中检查的所有列。

以下是您使用示例的方式:

CREATE TRIGGER event2uniqTrigger
       BEFORE INSERT ON event2
       WHEN ( 
    SELECT count( * )
      FROM event2 t
     WHERE ( CASE
                    WHEN new.a IS NULL THEN t.a IS NULL 
                    ELSE t.a = new.a 
           END ) 
           AND
           ( CASE
                    WHEN new.b IS NULL THEN t.b IS NULL 
                    ELSE t.b = new.b 
           END ) 
           AND
           ( CASE
                    WHEN new.c IS NULL THEN t.c IS NULL 
                    ELSE t.c = new.c 
           END )  
           AND
           ( CASE
                    WHEN new.d IS NULL THEN t.d IS NULL 
                    ELSE t.d = new.d 
           END )  
           AND
           ( CASE
                    WHEN new.e IS NULL THEN t.e IS NULL 
                    ELSE t.e = new.e 
           END )  
           AND
           ( CASE
                    WHEN new.f IS NULL THEN t.f IS NULL 
                    ELSE t.f = new.f 
           END )  
    ) > 0
BEGIN
    SELECT RAISE ( FAIL, 'inserted value not unique' );
END;

和索引:

CREATE INDEX event2idx ON event2 (a, b, c, d, e, f);

您可能还想为BEFORE UPDATE创建另一个触发器。它看起来就像上面的那个,除了BEFORE UPDATE部分。