【问题标题】:Sqlite NULL and unique? [closed]Sqlite NULL 和唯一? [关闭]
【发布时间】:2014-05-07 02:56:34
【问题描述】:

我注意到我可以在具有 UNIQUE 约束的列中包含 NULL 值:UNIQUE(col)

在某些情况下会产生任何问题吗?

【问题讨论】:

  • 什么情况?顺便说一句,数据类型是什么?
  • “在某些情况下会产生任何问题吗?”可能 - 您担心哪些情况?
  • 喜欢做任何类型的数据库操作。我会得到任何错误吗?当我不再需要它们时,我正在考虑将记录设置为 NULL。问题是删除它们需要花费大量时间,我宁愿在 cron 工作中这样做
  • 所以问题是任何不过滤掉 NULL 记录的查询都会看到“已删除”的记录。但这是一个应用程序问题,而不是数据库问题。我会做更多关于为什么删除需要这么长时间的研究 - DELETE 操作应该和查找记录一样快。
  • 我已经知道为什么了。这是因为外键约束和附加到每个相关记录的一些触发器:(

标签: sql sqlite null unique unique-constraint


【解决方案1】:

如果您希望您的唯一索引在您忽略 NULL 列(并且不想使用 Satyam 的答案中的触发器)时两行相同时引发错误,您可以执行类似的操作

CREATE TABLE `test` (
    `Field1`    INTEGER,
    `Field2`    INTEGER
);
CREATE UNIQUE INDEX `ix` ON `test` (
    `Field1`,
    `Field2`
);
INSERT INTO `test`(`Field1`,`Field2`) VALUES (1,NULL);
INSERT INTO `test`(`Field1`,`Field2`) VALUES (1,NULL); -- This shouldn't be allowed

DROP INDEX IF EXISTS `ix`;
CREATE UNIQUE INDEX `ix2` ON `test` (
    `Field1`,
    ifnull(`Field2`, 0)  --use this instead
); --will fail

【讨论】:

  • 非常巧妙的把戏。谢谢。
  • ifnull() 解决方案还会导致我在 'INSERT INTO test(Field1,Field2) VALUES (2,3);' 上的插入失败我不知道为什么,但这不是我所期望的。我在应用程序中而不是在数据库中解决了问题,但我想把它放在这里给任何追随我的人。
  • 另一个使用过滤索引的选项 (stackoverflow.com/a/15206061/2864740)。
  • 你说得对,这里有一个更好的链接sqlite.org/partialindex.html,也可以看看什么时候可以使用的说明
  • 好把戏。但是,可以通过删除不必要的反引号(或至少用标准双引号替换它们)并使用标准 coalesce(Field2,0) 而不是 ifnull() 来改进代码示例。
【解决方案2】:

您可以在表上创建 2 个触发器,以检查在任何插入或更新操作之前是否存在列为 null 的行,如果存在则引发异常。

CREATE TRIGGER UniqueColumnCheckNullInsert
        BEFORE INSERT
        ON 'Tablename'
        WHEN NEW.'column_name' IS NULL
        BEGIN
        SELECT CASE WHEN((
            SELECT 1
            FROM 'Tablename'
            WHERE 'column_name' IS NULL
            )
            NOTNULL) THEN RAISE(ABORT, "error row exists") END;
    END;

CREATE TRIGGER UniqueColumnCheckNullUpdate
        BEFORE UPDATE
        ON 'Tablename'
        WHEN NEW.'column_name' IS NULL
        BEGIN
        SELECT CASE WHEN((
            SELECT 1
            FROM 'Tablename'
            WHERE 'column_name' IS NULL
            )
            NOTNULL) THEN RAISE(ABORT, "error row exists") END;
    END;

【讨论】:

    【解决方案3】:

    虽然以下解决了多个空值,但它确实解决了与这种设计相关的任何“问题”,除了可能的数据库/SQL 可移植性 - 因此,它可能 被视为一个答案,留在这里仅供参考。


    这实际上在 SQLite 常见问题解答中有所涉及。这是一种设计选择 - SQLite(与 SQL Server 不同)选择多个 NULL 值计入索引中的唯一性。

    The SQL standard requires that a UNIQUE constraint be enforced even if one or more of the columns in the constraint are NULL, but SQLite does not do this. Isn't that a bug?

    也许您指的是 SQL92 中的以下语句:

    • 当且仅当表中没有两行在唯一列中具有相同的非空值时,才满足唯一约束。

    那句话是模棱两可的,至少有两种可能的解释:

    1. 当且仅当表中没有两行具有相同的值并且在唯一列中具有非空值时,才满足唯一约束。

    2. 当且仅当表中没有两行在不为空的唯一列的子集中具有相同的值时,才满足唯一约束。

    SQLite 遵循解释 (1),PostgreSQL、MySQL、Oracle 和 Firebird 也是如此。 Informix 和 Microsoft SQL Server 确实使用解释 (2),但是我们 SQLite 开发人员认为解释 (1) 是对需求最自然的解读,我们还希望最大限度地兼容其他 SQL 数据库引擎,以及大多数其他数据库引擎也与 (1) 一起使用,这就是 SQLite 所做的。

    查看comparison of NULL handling

    【讨论】:

    • 我想不出一种情况,其中 (1) 会是有用的解释。
    • 我有一个:用户有一张带有唯一 id 的钥匙卡,这意味着没有两个用户可以拥有相同的钥匙。但是有些用户没有钥匙卡了(NULL)。他们可能已经把钥匙卡给了另一个用户。
    • @JohannesEgger SQL Server 的过滤索引 (stackoverflow.com/a/15206061/2864740) 对行为 (1) 语义非常有用的时代之一。
    • 这看起来很棒,更强大,更明确。感谢您指出这一点?
    猜你喜欢
    • 2010-09-07
    • 1970-01-01
    • 1970-01-01
    • 2021-05-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-07
    相关资源
    最近更新 更多