2010-10-23 65 views
3

我有一个带有“position”列的表“items”。位置具有唯一的非空约束。为了在位置插入一个新行X我第一次尝试增加后续项目的位置:在PostgreSQL中使用非空和唯一约束来增加字段8.3

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

这将导致违反唯一约束:

ERROR: duplicate key value violates unique constraint 

这个问题似乎是为了PostgreSQL在其中执行更新。 PostgreSQL < 9.0中的唯一限制是不可延迟的,不幸的是,使用9.0目前不是一种选择。此外,UPDATE语句不支持ORDER BY子句和以下不工作,太(还是重复键冲突):

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

是否有人知道一个解决方案,不涉及迭代所有条目在代码中?

+0

'position'总是一个整数吗? – SimonJ 2010-10-23 20:50:42

+0

是的,位置总是一个整数。它用于让用户定义项目的自定义顺序。 – lassej 2010-10-23 20:53:18

回答

3

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

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) 

希望它有帮助。

+0

谢谢,我会尽力的。本来希望没有存储过程是可能的。 – lassej 2010-10-24 19:49:57

2


为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) 

现在整个魔法:

​​

之后我们有:

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) 

该解决方案有一个缺点:它需要锁定整个表格,但这可能不是问题。

+0

感谢您的回答,但我不希望在每次交易中删除并重新应用约束。上面的例子是一个简化的例子。我在[foreign_key,position]上有一个复合唯一索引,并且随着行数的增加,这种方法可能会变得很慢。 – lassej 2010-10-24 09:15:08

+0

确实......我会在下一个答案中插入另一个解决方案,这里我没有代码高亮。 – 2010-10-24 10:12:53

0

的“correcter的解决方案可能是,使得约束DEFERRABLE

ALTER TABLE channels ADD CONSTRAINT 
channels_position_unique unique("position") 
DEFERRABLE INITIALLY 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;