【问题标题】:SQLite long-arm check constraint?SQLite 长臂检查约束?
【发布时间】:2019-06-03 10:29:18
【问题描述】:

我在 SQLite 中有两个表,建模一对多关系:

CREATE TABLE parent (
    id INTEGER PRIMARY KEY,
    payload TEXT
);

CREATE TABLE child (
    id INTEGER PRIMARY KEY,
    flag BOOLEAN,
    parent_id INTEGER,
    FOREIGN KEY(parent_id) REFERENCES parent (id) ON DELETE CASCADE ON UPDATE CASCADE, 
);

有没有办法将CHECK CONSTRAINT 放在child.flag 上,这样在所有child 中对于任何parent 总是只有一个True

【问题讨论】:

  • CHECK 约束的表达式可能不包含子查询。 所以,不。不过,您也许可以使用触发器来获得相同的效果。

标签: sql sqlite check-constraints


【解决方案1】:

“虽然这是通过放应用级逻辑来解决的,但我还是想看看纯数据库有没有创造性的解决方案,不涉及应用和触发器。”

是的,使用partial unique index 可以实现所需的约束:

CREATE UNIQUE INDEX idx ON child (flag, parent_id) WHERE flag = 1;

db<>fiddle demo

INSERT INTO parent(id, payload) VALUES(1, 'Parent1');
INSERT INTO child(id, flag, parent_id) VALUES (1, 1, 1);
INSERT INTO child(id, flag, parent_id) VALUES (2, 0, 1);
INSERT INTO child(id, flag, parent_id) VALUES (3, 0, 1);
SELECT * FROM child;

-- trying to insert second active child will cause unique index violation
INSERT INTO child(id, flag, parent_id) VALUES (4, 1, 1)
-- UNIQUE constraint failed: child.flag, child.parent_id

【讨论】:

  • 这个索引似乎只解决了一半的问题,即确保有only one 条目带有标志。如何确保有always one?从我的阅读中,它似乎并没有阻止删除?
  • @JinghuiNiu 您是否知道,如果以声明方式定义此类要求,则需要立即执行删除操作和更新/插入(如 MERGE)或一次更新两条记录(使用标志 =1 设置新记录并立即将现有的 1 设置为 0)。更多信息:stackoverflow.com/a/10292449/5070879
  • 所以你的意思是,我们被困在这个悲惨的 SQL 世界里了?:)
  • @JinghuiNiu 我强烈反对悲惨的SQL世界。它是强大的描述性语言。关键是您提出的要求不能以声明方式实现(您可以使用存储过程/触发器编写代码)来保护 1:1 部分关系。如果您希望它作为声明性部分,那么您需要multirows constraints aka. assertions(SQLite 不支持)。
  • @JinghuiNiu 更新/删除现有值需要一次处理多行(当然可以通过延迟约束来实现),并且在提交时一切都会到位。
【解决方案2】:

我的研究表明,没有办法让本地检查约束知道其兄弟状态。我建议将检查逻辑放在应用层。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-12-03
    • 1970-01-01
    • 1970-01-01
    • 2021-07-02
    • 2011-01-28
    • 2023-03-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多