JSONB空字段的唯一约束

时间:2018-08-07 02:32:21

标签: postgresql database-design null unique-constraint jsonb

让我用一个简单的示例来描述我的问题。假设我有一个表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”为空,也可以使唯一约束生效。

1 个答案:

答案 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)约束,默认值等等。