让我用一个简单的示例来描述我的问题。假设我有一个表t,其中只有两个字段,即自动“ id”和一个jsonb类型的“ info”字段:
CREATE TABLE t(
ID serial NOT NULL PRIMARY KEY,
info jsonb NOT NULL
);
'info'可能具有字段'a','b','c'以及其他一些字段。字段'c'可能为空,我想(a,b,c)是唯一的,所以我在它们上创建了唯一的索引:
CREATE UNIQUE INDEX idx_abc ON t (
(info ->> 'a'), (info ->> 'b'), (info ->> 'c')
);
但是后来我意识到,如果'c'为null,则此唯一约束根本不起作用。例如,我可以插入以下行:
insert into t(info) values('{"a": "a1", "b": "b1"}')
或
insert into t(info) values('{"a": "a1", "b": "b1", "c": null}')
我当前的解决方案是无论如何都给“ c”一个值,如果“ c”为空,那么我设置为“空”:
insert into t(info) values('{"a": "a1", "b": "b1", "c": "null"}')
如果我运行两次插入,这种方式将触发唯一约束。但是我觉得这只是一种解决方法,我想知道是否有更好的解决方案,即使字段“ c”为空,也可以使唯一约束生效。
答案 0 :(得分:3)
如果只是一个可能具有NULL值的键,则部分索引将是一种有效的解决方案,如下所示:
更通用(效率较低)的解决方案是使用COALESCE
:
CREATE UNIQUE INDEX idx_abc ON t (
COALESCE(info ->> 'a', 'NULL')
, COALESCE(info ->> 'b', 'NULL')
, COALESCE(info ->> 'c', 'NULL')
);
这样,您将实际的NULL
值和字符串'NULL'(任意示例)折叠为相同的值。就像您已经做过的一样,但是您无需弄乱原始的列值。
我还将考虑从jsonb
文档中提取涉及的键作为常规的Postgres列(无论是否冗余),以提供关系工具的全部功能:(NOT NULL
)约束,默认值等等。