在 PostgreSQL 中移动(更新)唯一列值
Shift (update) unique column values in PostgreSQL
使用 MS SQL 服务器,以下工作正常:
CREATE TABLE #temptable(mykey int primary key)
INSERT INTO #temptable VALUES (1)
INSERT INTO #temptable VALUES (2)
UPDATE #temptable SET mykey=mykey+1
但是,使用 PostgreSQL,以下失败:
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.
我需要在一个 table 中递增一列的每个值,这是复合唯一约束的一部分。我怎样才能在一条语句中做到这一点?
谢谢!
编辑:如果您想知道为什么这有意义(至少对我而言),这里有一个更完整的场景。
我有一个 table 项目是按类别组织的。每个项目在类别中都有特定的位置。
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
此 table 包含如下数据:
category1 : (foo, bar), (foo2, bar2)
category2 : (foo4, bar4), (foo3, bar3)
请注意,在类别 2 中,(foo4, bar4) 出现在 (foo3, bar3) 之前。
现在,如果我想重新排序一个类别中的项目,我需要更新 category_position... 但是由于 PK,我无法像使用 SQL Server 那样使用 PostgreSQL 移动值.
这确实有点令人困惑,因为所有其他约束都是在语句级别上评估的,只有 PK/unique 约束在 DML 操作期间在每行级别上评估。
但是您可以通过将主键约束声明为可延迟来解决这个问题:
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;
延迟约束确实有一些开销,因此通过将其定义为 initially immediate
,此开销保持在最低限度。您可以在需要时使用 set constraint
.
延迟约束评估
但真正的问题是:为什么需要对主键值执行此操作? PK 值没有任何意义,因此似乎没有必要增加所有值(无论使用的是什么 DBMS)
不改变约束的解决方案 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
使用 MS SQL 服务器,以下工作正常:
CREATE TABLE #temptable(mykey int primary key)
INSERT INTO #temptable VALUES (1)
INSERT INTO #temptable VALUES (2)
UPDATE #temptable SET mykey=mykey+1
但是,使用 PostgreSQL,以下失败:
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.
我需要在一个 table 中递增一列的每个值,这是复合唯一约束的一部分。我怎样才能在一条语句中做到这一点?
谢谢!
编辑:如果您想知道为什么这有意义(至少对我而言),这里有一个更完整的场景。
我有一个 table 项目是按类别组织的。每个项目在类别中都有特定的位置。
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
此 table 包含如下数据:
category1 : (foo, bar), (foo2, bar2)
category2 : (foo4, bar4), (foo3, bar3)
请注意,在类别 2 中,(foo4, bar4) 出现在 (foo3, bar3) 之前。 现在,如果我想重新排序一个类别中的项目,我需要更新 category_position... 但是由于 PK,我无法像使用 SQL Server 那样使用 PostgreSQL 移动值.
这确实有点令人困惑,因为所有其他约束都是在语句级别上评估的,只有 PK/unique 约束在 DML 操作期间在每行级别上评估。
但是您可以通过将主键约束声明为可延迟来解决这个问题:
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;
延迟约束确实有一些开销,因此通过将其定义为 initially immediate
,此开销保持在最低限度。您可以在需要时使用 set constraint
.
但真正的问题是:为什么需要对主键值执行此操作? PK 值没有任何意义,因此似乎没有必要增加所有值(无论使用的是什么 DBMS)
不改变约束的解决方案 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