【问题标题】:Increment field with not null and unique constraint in PostgreSQL 8.3PostgreSQL 8.3 中具有非空和唯一约束的增量字段
【发布时间】:2011-04-29 16:34:10
【问题描述】:

我有一个表“项目”,其中有一列“位置”。 position 具有唯一且非空的约束。为了在位置 x 插入新行,我首先尝试增加后续项目的位置:

UPDATE items SET position = position + 1 WHERE position >= x;

这会导致违反唯一约束:

ERROR:  duplicate key value violates unique constraint

问题似乎是 PostgreSQL 执行更新的顺序。 PostgreSQL

UPDATE items SET position = position + 1 WHERE id IN (
  SELECT id FROM items WHERE position >= x ORDER BY position DESC)

有人知道不涉及迭代代码中所有项目的解决方案吗?

【问题讨论】:

  • 'position' 总是一个整数吗?
  • 是的,位置总是一个整数。它用于让用户定义项目的自定义顺序。

标签: sql postgresql sql-update unique-constraint


【解决方案1】:


由于 PostgreSQL 支持全套事务性 DDL,您可以轻松地执行以下操作:

create table utest(id integer unique not null);
insert into utest(id) select generate_series(1,4);

表格现在看起来像这样:

test=# \d utest
     Table "public.utest"
 Column |  Type   | Modifiers 
--------+---------+-----------
 id     | integer | not null
Indexes:
    "utest_id_key" UNIQUE, btree (id)

test=# select * from utest;
 id 
----
  1
  2
  3
  4
(4 rows)

现在是整个魔法:

begin;
alter table utest drop constraint utest_id_key;
update utest set id = id + 1;
alter table utest add constraint utest_id_key unique(id);
commit;

之后我们有:

test=# \d utest
     Table "public.utest"
 Column |  Type   | Modifiers 
--------+---------+-----------
 id     | integer | not null
Indexes:
    "utest_id_key" UNIQUE, btree (id)

test=# select * from utest;
 id 
----
  2
  3
  4
  5
(4 rows)

这个方案有一个缺点:需要锁住整张表,不过这里可能不成问题。

【讨论】:

  • 感谢您的回答,但我不希望在每个事务中删除并重新应用约束。上面的例子是一个简化的例子。我在 [foreign_key, position] 上有一个复合唯一索引,随着行数的增加,这种方法可能会很快变慢。
  • 确实...我会在下一个答案中插入另一个解决方案,这里我没有代码高亮。
【解决方案2】:

另一个表,有多个唯一索引:

create table utest(id integer, position integer not null, unique(id, position));
test=# \d utest
      Table "public.utest"
  Column  |  Type   | Modifiers 
----------+---------+-----------
 id       | integer | 
 position | integer | not null
Indexes:
    "utest_id_key" UNIQUE, btree (id, "position")

一些数据:

insert into utest(id, position) select generate_series(1,3), 1;
insert into utest(id, position) select generate_series(1,3), 2;
insert into utest(id, position) select generate_series(1,3), 3;

test=# select * from utest order by id, position;
 id | position 
----+----------
  1 |        1
  1 |        2
  1 |        3
  2 |        1
  2 |        2
  2 |        3
  3 |        1
  3 |        2
  3 |        3
(9 rows)

我创建了一个以正确顺序更新位置值的过程:

create or replace function update_positions(i integer, p integer) 
  returns void as $$
declare
  temprec record;
begin
  for temprec in 
    select * 
      from utest u 
      where id = i and position >= p 
      order by position desc 
  loop
    raise notice 'Id = [%], Moving % to %', 
      i, 
      temprec.position, 
      temprec.position+1;

    update utest 
      set position = position+1 
      where position=temprec.position and id = i;
  end loop;
end;
$$ language plpgsql;

一些测试:

test=# select * from update_positions(1, 2);
NOTICE:  Id = [1], Moving 3 to 4
NOTICE:  Id = [1], Moving 2 to 3
 update_positions 
------------------

(1 row)

test=# select * from utest order by id, position;
 id | position 
----+----------
  1 |        1
  1 |        3
  1 |        4
  2 |        1
  2 |        2
  2 |        3
  3 |        1
  3 |        2
  3 |        3
(9 rows)

希望对你有帮助。

【讨论】:

  • 谢谢,我会试试的。曾希望没有存储过程也能实现。
【解决方案3】:

“更正”解决方案可能是使约束DEFERRABLE

ALTER TABLE channels ADD CONSTRAINT 
channels_position_unique unique("position") 
DEFERRABLE INITIALLY IMMEDIATE

然后在递增时将该约束设置为 DEFERRED,并在完成后将其设置回 IMMEDIATE。

SET CONSTRAINTS channels_position_unique DEFERRED;
UPDATE channels SET position = position+1 
WHERE position BETWEEN 1 AND 10;
SET CONSTRAINTS channels_position_unique IMMEDIATE;

【讨论】:

    【解决方案4】:

    不改变表和删除约束的变体:

    UPDATE items t1 
    SET    position = t2.position + 1 
    FROM   (SELECT position
        FROM   items 
        ORDER  BY position DESC) t2 
    WHERE t2.position >= x AND t1.position = t2.position
    

    在线示例:http://rextester.com/FAU54991

    【讨论】:

      猜你喜欢
      • 2011-03-18
      • 1970-01-01
      • 1970-01-01
      • 2013-12-16
      • 2014-12-16
      • 1970-01-01
      • 2016-08-29
      • 2018-05-28
      • 2013-12-22
      相关资源
      最近更新 更多