Shift (update) unique column values in PostgreSQL

99封情书 提交于 2019-11-27 08:03:20

问题


Using MS SQL Server, the following works fine:

CREATE TABLE #temptable(mykey int primary key)

INSERT INTO #temptable VALUES (1)
INSERT INTO #temptable VALUES (2)

UPDATE #temptable SET mykey=mykey+1

However, using PostgreSQL, the following fails:

CREATE TABLE pg_temp.tbl_test(testkey integer primary key)

INSERT INTO pg_temp.tbl_test VALUES (1)
INSERT INTO pg_temp.tbl_test VALUES (2)

UPDATE pg_temp.tbl_test SET testkey=testkey+1

ERROR: duplicate key value violates unique constraint "tbl_test_pkey" DETAIL: Key (testkey)=(2) already exists.

I need to increment every value of one column in one table, which is part of a composite unique constraint. How can I do this in one statement ?

Thanks !


Edit: If you are wondering why this makes sense (at least to me), here is a more complete scenario.

I have one table of items organized in categories. Each item has a particular position in the category.

category_id (PK) | category_position (PK) | item_attribute_1 | item_attribute_2
1 | 1 | foo | bar
1 | 2 | foo2 | bar2
2 | 1 | foo4 | bar4
2 | 2 | foo3 | bar3

This table contains data like:

category1 : (foo, bar), (foo2, bar2)
category2 : (foo4, bar4), (foo3, bar3)

Note that (foo4, bar4) comes before (foo3, bar3) in category2. Now if I want to reorder items in one category, I need to update category_position... But because of the PK, I cannot shift values using PostgreSQL as I could with SQL Server.


回答1:


This is indeed a bit confusing as all other constraints are evaluated on a statement level, only PK/unique constraint are evaluated on a per row level during DML operations.

But you can work around that by declaring the primary key constraint as deferrable:

create table tbl_test 
(
  testkey   INTEGER,
  constraint pk_tbl_test primary key (testkey) deferrable initially immediate
);

insert into tbl_test values (1), (2);

set constraints all deferred;

update tbl_test
   set testkey = testkey +1;

Deferred constraints do have some overhead, so by defining it as initially immediate this overhead is kept to a minimum. You can the defer the constraint evaluation when you need it by using set constraint.


The real question however is: why would you need to do this on a primary key value? The PK values has no meaning whatsoever, so it seems rather unnecessary to increment all values (regardless of the DBMS being used)




回答2:


Solution without altering constraint as deferrable initially immediate

UPDATE tbl_test t1 
SET    testkey = t2.testkey + 1 
FROM   (SELECT testkey 
    FROM   tbl_test 
    ORDER  BY testkey DESC) t2 
WHERE  t1.testkey = t2.testkey 

Online example: http://rextester.com/edit/GMJ48099



来源:https://stackoverflow.com/questions/31471438/shift-update-unique-column-values-in-postgresql

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!