简体   繁体   English

如何创建即使列为NULL仍然有效的UNIQUE约束

[英]How to create a UNIQUE constraint which works even if columns are NULL

I have a table which I want to prevent duplicate rows from being inserted on. 我有一个表,希望防止插入重复的行。

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

Now I insert a couple rows: 现在我插入几行:

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

Now if I re-run the above command, I would expect it to not insert anything, because the new rows exactly match what is already there. 现在,如果我重新运行上面的命令,我希望它不会插入任何内容,因为新行与当前的行完全匹配。 Instead, I see two new rows inserted. 相反,我看到插入了两个新行。

Reading the SQLite documentation , it appears that SQLite does not enforce UNIQUE constraints if one or more of the columns are NULL. 阅读SQLite文档时 ,如果其中一列或多列为NULL,则SQLite似乎不强制执行UNIQUE约束。 If there some way to achieve this goal using some other mechanism? 是否有其他方法可以使用其他机制来实现此目标? In the above case, the c , d , e , and f columns could be null. 在上述情况下, cdef列可以为空。 Alternatively I could use empty strings instead of NULL but reading some other answers seemed to suggest that null shouldn't be confused with the empty string as they denote separate things. 另外,我可以使用空字符串而不是NULL,但阅读其他答案似乎表明,不应将null与空字符串混淆,因为它们表示单独的内容。

You can use BEFORE INSERT trigger to check data correctness. 您可以使用BEFORE INSERT触发器来检查数据的正确性。

Be aware, that this does full table scan before every insert, so for bigger tables it will cause inserts to be slow! 请注意,这会在每次插入之前进行全表扫描,因此对于较大的表,这将导致插入速度变慢!

To speed it up (and avoid full table scans) you should CREATE INDEX , which will include all columns checked in your trigger. 为了加快速度(并避免全表扫描),您应该CREATE INDEX ,它将包括触发器中选中的所有列。

Here's how you do it with your example: 使用示例的方法如下:

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;

and the index: 和索引:

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

You may also want to create another trigger for BEFORE UPDATE . 您可能还想为BEFORE UPDATE创建另一个触发器。 It would look just like the one above, except the BEFORE UPDATE part. 除了“ BEFORE UPDATE部分外,它看起来像上面的那个。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM