JSONB 空字段的唯一约束
Unique constraint on JSONB null field
让我用一个简单的例子来描述我的问题。假设我有一个 table t,它只有两个字段,auto '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"为null,那么我设置"null":
insert into t(info) values('{"a": "a1", "b": "b1", "c": "null"}')
如果我 运行 插入两次,这种方式将触发唯一约束。但我觉得这只是一种解决方法,我想知道是否有更好的解决方案来允许唯一约束工作,即使字段 "c" 为空。
如果只有一个键可能具有 NULL 值,部分索引将是一种有效的解决方案,如下所述:
- Create unique constraint with null columns
更通用(效率较低)的解决方案是使用 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
) 约束,默认值等。也更快。
让我用一个简单的例子来描述我的问题。假设我有一个 table t,它只有两个字段,auto '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"为null,那么我设置"null":
insert into t(info) values('{"a": "a1", "b": "b1", "c": "null"}')
如果我 运行 插入两次,这种方式将触发唯一约束。但我觉得这只是一种解决方法,我想知道是否有更好的解决方案来允许唯一约束工作,即使字段 "c" 为空。
如果只有一个键可能具有 NULL 值,部分索引将是一种有效的解决方案,如下所述:
- Create unique constraint with null columns
更通用(效率较低)的解决方案是使用 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
) 约束,默认值等。也更快。