我在更大的数据库中遇到了唯一的索引违规。原始问题发生在存储的pl / pgsql函数中。
我简化了一切以显示我的问题。我可以在一个相当简单的表格中重现它:
CREATE TABLE public.test
(
id integer NOT NULL DEFAULT nextval('test_id_seq'::regclass),
pos integer,
text text,
CONSTRAINT text_pkey PRIMARY KEY (id)
)
WITH (
OIDS=FALSE
);
ALTER TABLE public.test
OWNER TO root;
GRANT ALL ON TABLE public.test TO root;
我在' pos':
上定义了一个唯一索引CREATE UNIQUE INDEX test_idx_pos
ON public.test
USING btree
(pos);
在UPDATE之前,表格中的数据如下所示:
testdb=# SELECT * FROM test;
id | pos | text
----+-----+----------
2 | 1 | testpos1
3 | 2 | testpos2
1 | 5 | testpos4
4 | 4 | testpos3
(4 Zeilen)
tr: (4 rows)
现在我想减少所有' pos'值大于2并且得到错误(tr是我从德语到英语的翻译):
testdb=# UPDATE test SET pos = pos - 1 WHERE pos > 2;
FEHLER: doppelter Schlüsselwert verletzt Unique-Constraint »test_idx_pos«
tr: ERROR: duplicate key violates unique constraint »test_idx_pos«
DETAIL: Schlüssel »(pos)=(4)« existiert bereits.
tr: key »(pos)=(4) already exists.
如果UPDATE运行完毕,表格将如下所示并再次成为唯一:
testdb=# SELECT * FROM test;
id | pos | text
----+-----+----------
2 | 1 | testpos1
3 | 2 | testpos2
1 | 4 | testpos4
4 | 3 | testpos3
(4 Zeilen)
tr: (4 rows)
我该如何避免这种情况?我了解到存储的pl / pgsql函数嵌入到事务中,所以这个问题不应该出现?
答案 0 :(得分:4)
每行评估唯一索引而不是每个语句(例如,与Oracle的实现不同)
此问题的解决方案是使用唯一的约束,它可以延迟,因此在事务结束时进行评估。
如此而不是唯一索引,定义一个约束:
alter table test add constraint test_idx_pos unique (pos)
deferrable initially deferred;