1

我正在尝试有条件地将一些数据插入到表中,其中列值的每个组合最多只能在表中出现一次。架构看起来像这样:

CREATE TABLE foobar (
    id SERIAL PRIMARY KEY,
    a_id INTEGER,
    b_id INTEGER,
    c_id INTEGER,
    ident VARCHAR(32),
    date_a timestamp,
    date_b timestamp,
    FOREIGN KEY a_id REFERENCES a (id) ON DELETE CASCADE,
    FOREIGN KEY b_id REFERENCES b (id) ON DELETE CASCADE,
    FOREIGN KEY c_id REFERENCES c (id) ON DELETE CASCADE));

(a_id, b_id, c_id, ident) 的组合是唯一的,但仅适用于 date_a AND date_b 均为 NULL 的行。

只有当 a_id、b_id、c_id、任务组合不在数据库中时,我才希望能够插入新行。如果是,它不需要做任何事情。

起初我尝试在这些列上创建唯一约束,但问题是允许 a_id、b_id 和 c_id 为 NULL,只要其中至少一个不为空。这破坏了唯一约束。因为 a、b 和 c_id 字段是外键,所以我不能将它们设置为其他一些存根值(如 -1)。

我尝试使用锁定(与我更好的判断相反),这在测试的几分钟内导致了死锁。

这个问题有没有标准的解决方案?

4

2 回答 2

1

您可能应该在要检查的确切条件上使用唯一索引,而不是使用唯一约束。然后,您可以将空值合并为一个虚拟值,例如 -1。就像是:

CREATE UNIQUE INDEX
foobar_test ON foobar
(COALESCE(a_id, -1), COALESCE(b_id, -1), COALESCE(c_id, -1), ident) -- Nulls become -1
WHERE date_a is null and date_b is null; -- Only check when date_a and date_b is null

这将确保a_id, b_id, c_id, 和ident是唯一的(包括它们的空值组合)对于所有date_adate_b都是的行null

于 2013-03-18T16:35:51.933 回答
0

在我的多个生产数据库中的类似情况下,我只是将“默认行”添加到引用的表中。我使用id = 0这些并在 10 或 100 处开始真正的代理键。或者,如果保留,您可以使用-1(或任何适合您的) 。0

这样,我可以将所有 fk 列设置为开箱即用的NOT NULL DEFAULT 0(部分)约束:UNIQUE

CREATE UNIQUE INDEX tbl_uni_idx ON tbl (a_id, b_id, c_id, ident)
WHERE date_a IS NULL AND date_b IS NULL;
于 2013-03-18T17:07:01.443 回答