我有以下代码,它会引发错误:
错误:没有与引用表“person”的给定键匹配的唯一约束
DROP TABLE IF EXISTS person;
CREATE TABLE person (
sn serial PRIMARY KEY,
id int NOT NULL,
modified timestamp,
name text
);
CREATE UNIQUE INDEX person_id_key ON person (id) WHERE modified IS NULL;
DROP TABLE IF EXISTS account;
CREATE TABLE account (
id int NOT NULL REFERENCES person(id),
name text
);
有一个唯一索引id
:
Table "public.person"
Column | Type | Collation | Nullable | Default
----------+-----------------------------+-----------+----------+------------------------------------
sn | integer | | not null | nextval('person_sn_seq'::regclass)
id | integer | | not null |
modified | timestamp without time zone | | |
name | text | | |
Indexes:
"person_pkey" PRIMARY KEY, btree (sn)
"person_id_key" UNIQUE, btree (id) WHERE modified IS NULL
为什么外键不够?
这有点像怪物 - < TL:DR> 转到附录 </TL:DR>
你的问题:
因为,您的索引
person_id_key UNIQUE, btree (id) WHERE modified IS NULL
仅涵盖一小部分记录的唯一性person.id
。为了使字段成为FOREIGN KEY
,它必须覆盖所有记录person.id
!您必须做一些不同的事情,并在表创建时
CREATE
创建UNIQUE
索引,然后创建部分索引(显然,并且使用不同的名称)。person.id
CREATE
所有的代码都在这里。
你所做的是这样的:
然后是
CREATE
你的账户表:当成功后,现在我们
CREATE
的部分索引:然后我们运行以下查询来检查:
结果:
瞧!您可以通过运行
\set ECHO_HIDDEN on
inpsql
然后运行 来获取上面的 SQL\di
。设置ECHO_HIDDEN
变量意味着当您发出psql
元命令(即不是 SQL)时,会给出该命令生成的 SQL - 这是学习 PostgreSQL 的好方法!有几点:
我可以建议您使用
TIMESTAMPTZ
(带时区)而不只是TIMESTAMP
- 这只是更好的做法 - 即使您从未计划搬到不同的国家/地区,仍然需要考虑 DST(夏令时)问题。而且,即使您居住的地区现在没有夏令时,将来也可能会实行另外,我可以建议您为索引指定有意义的名称,例如将
UNIQUE
索引添加为_uq
或类似的后缀。这也有效!
附录 - 回应评论中OP的问题:
对于那些已经忘记的人来说,第一段是:
在你的问题促使我进行调查之前,我真的从未深入思考过这个问题 - 我真的很高兴我这么做了。我的发现让我感到惊讶,但我也很感激我的分析结果让我有了一种顿悟。
我的第一条线索来自@ErwinBrandstetter,他写道:
UNIQUE CONSTRAINT
现在,请注意 a和 a之间的区别UNIQUE INDEX
- 我花了更多的搜索、修改和阅读才能最终掌握它。他接着说:
因此,再次进行区分 - 不确定他所说的“没有......索引类型”是什么意思。
从这里(再次@EB),我们有:
因此, a
CONSTRAINT
有一个底层INDEX
,但它本身不仅仅是一个索引 - 它不仅仅是一个索引!进一步的线索可以在这里找到:
EB(第一个链接)说:
因此,这是
UNIQUE
约束和索引之间的两个记录差异。当我偶然发现这颗宝石时,一切终于对我来说一切就位了——它证明了两者之间存在很大的区别:
因此,根据您的情况,我修改了原来的小提琴并包含了我学到的内容。
我用小提琴掉进了兔子洞,所以你可以忽略除了最后一个片段(上面的 SQL)之外的所有内容 - 请注意,现在有 2 条记录,而不是3 条记录!
SQL 结果(仅显示前两个字段):
其中一个代表表
PRIMARY KEY
的person
,另一个代表(整体)UNIQUE CONSTRAINT
- “缺少”的是部分UNIQUE INDEX
,那是因为它不是约束!我对自己合理化的方式是, a
CONSTRAINT
能够阻止东西进入表中 - 一旦它通过了所有约束,INDEX
es 的工作就是提供指针,以便如果优化器可以更快地访问它认为应该使用它 - 即仅仅是实现细节。最后的小提琴- 我将其更改为
ALTER TABLE ADD CONSTRAINT...
,CREATE INDEX...
现在 SQL 中只有 1 条记录CONSTRAINT
- 但帐户FK
不会失败 - 因为它覆盖了整个表。CONSTRAINT
s = 保护INDEX
s = 组织(除非它们覆盖整个表格)CONSTRAINT
s - SQL 标准强制要求INDEX
es -未强制要求所以,回答问题
参考?是的 - 请参阅链接
两种类型的
UNIQUE
索引?其实,更准确的说法是,分为三种UNIQUE CONSTRAINT
s - 可用于FK
sUNIQUE INDEX
es(未限定)- 可用于FK
sUNIQUE INDEX
FK
es(部分)- 不能在s中使用瞧,+1 真的让我思考!
其他有用的链接:
https://stackoverflow.com/questions/53602863/how-do-i-see-the-comment-on-a-postgresql-constraint
https://stackoverflow.com/questions/44274080/postgres-hash-index-with-unique-constraint
https://stackoverflow.com/questions/44274080/postgres-hash-index-with-unique-constraint