Postgresql 中 JSON 数据中的主键

dav*_*ley 5 constraint postgresql-9.3 json

我有一个表t,其中有一列名为json,类型为JSON。在 JSON 中有一个自然键

> SELECT json->'id' AS id FROM t LIMIT 1;
             id              
-----------------------------
 " 63631ff3809de7a17398602f"
Run Code Online (Sandbox Code Playgroud)

我可以创建一个UNIQUE INDEXon id,因此:

> CREATE UNIQUE INDEX t_id ON t((json->>'id'));                                                                                                                       

CREATE INDEX
Run Code Online (Sandbox Code Playgroud)

我想将其添加为table_constraint_using_index,但两者都失败了PRIMARY KEY

> ALTER TABLE t ADD CONSTRAINT t_pkey PRIMARY KEY USING INDEX t_id;                                                                                   
ERROR:  index "t_id" contains expressions
LINE 1: ALTER TABLE t ADD CONSTRAINT t_pkey
                                          ^
DETAIL:  Cannot create a primary key or unique constraint using such an index.
Run Code Online (Sandbox Code Playgroud)

UNIQUE

> ALTER TABLE t ADD CONSTRAINT t_unique_id UNIQUE USING INDEX t_id;
ERROR:  index "t_id" contains expressions
LINE 1: ALTER TABLE t ADD CONSTRAINT t_unique_id...
                                          ^
DETAIL:  Cannot create a primary key or unique constraint using such an index.
Run Code Online (Sandbox Code Playgroud)

我应该能够添加这样的约束吗?

dav*_*ley 6

正如克雷格的回答中所解释的,既PRIMARY KEY不能UNIQUE也不能使用。

但是,这里有一个两部分的解决方案来解决这个问题:

在此之后,一个\d ton inpsql会给你:

Indexes:
    "t_id" UNIQUE, btree ((json ->> 'id'::text))
Check constraints:
    "id_not_null" CHECK ((json ->> 'id'::text) IS NOT NULL)
Run Code Online (Sandbox Code Playgroud)

你可以看到not nulluniqueness都被强制执行:

test=# INSERT INTO t VALUES ('{"no id" : "field here"}');
ERROR:  new row for relation "t" violates check constraint "id_not_null"
DETAIL:  Failing row contains ({"no id" : "field here"}).

test=# INSERT INTO t VALUES ('{"id" : "abc"}');
INSERT 0 1

test=# INSERT INTO t VALUES ('{"id" : "abc"}');
ERROR:  duplicate key value violates unique constraint "t_id"
DETAIL:  Key ((json ->> 'id'::text))=(abc) already exists
Run Code Online (Sandbox Code Playgroud)

* 请注意,仅存在t_id索引就足以强制执行唯一性,即使它没有明确作为约束给出。


Cra*_*ger 5

不,您不应该添加这样的约束。

PRIMARY KEY必须是简单或复合索引。它可能不是部分或表达式索引。

索引充当约束,在功能上它们大致相同,但它不能PRIMARY KEY在表元数据中出现,也不能用作外键约束的目标。对于UNIQUE约束也是如此。

这里的问题是,的SQL标准的定义PRIMARY KEYUNIQUE约束不允许表达式或行匹配谓词。因此,如果 PostgreSQL 将表达式索引或部分索引列为约束,那么它就违反了标准,并向应用程序谎报了它在做什么。了解 PostgreSQL 特性的应用程序可以从 Pg 自己的目录中查找索引,并且 中也有信息information_schema,但它不能作为列出的约束进入。