【问题标题】:Shift (update) unique column values in PostgreSQL在 PostgreSQL 中移动(更新)唯一列值
【发布时间】:2015-07-17 08:29:05
【问题描述】:

使用 MS SQL Server,以下工作正常:

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

错误:重复键值违反唯一约束“tbl_test_pkey” 详细信息:密钥 (testkey)=(2) 已存在。

我需要增加一个表中一列的每个值,这是复合唯一约束的一部分。我怎样才能在一个语句中做到这一点?

谢谢!


编辑:如果您想知道为什么这是有道理的(至少对我而言),这里有一个更完整的场景。

我有一张按类别组织的项目表。每个项目在类别中都有特定的位置。

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

此表包含如下数据:

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

请注意, (foo4, bar4) 在 category2 中位于 (foo3, bar3) 之前。 现在,如果我想对一个类别中的项目重新排序,我需要更新 category_position...但是由于 PK,我不能像使用 SQL Server 那样使用 PostgreSQL 移动值。

【问题讨论】:

    标签: sql postgresql sql-update unique-constraint


    【解决方案1】:

    这确实有点令人困惑,因为所有其他约束都是在语句级别上评估的,在 DML 操作期间,只有 PK/唯一约束在每行级别上进行评估。

    但您可以通过将主键约束声明为可延迟来解决此问题:

    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)

    【讨论】:

    • 感谢您的回答;我在我的问题中添加了一个更完整的场景。我会看看你的解决方案。
    • @personne3000:可延迟属性在 constraint 级别上起作用,因此 PK 有多少列并不重要。但将位置作为PK的一部分似乎是一个相当奇怪的设计。 PK 值意味着稳定,而不是经常变化。每次插入新项目时都必须更改 PK 值通常表明数据库设计不佳。
    • 但是我仍然希望在这些列上保留一个 UNIQUE 约束,因为我不希望 2 个项目在同一类别中具有相同的位置。我确实有另一个 pkey 候选元组(它有另一个 UNIQUE 约束);改用它会更好吗?
    • @personne3000:如果您有一对“稳定”的 PK 列,那么我更愿意。您仍然可以在 category_id/position 上定义可延迟的唯一约束(不是索引!)。但是,如果您要经常重新编号位置,为什么首先需要唯一约束?当您重新编号时,您可以为每个项目分配唯一的位置。
    • 我最后将另一个元组用于 PK,并按照您的建议为类别和顺序使用了可延迟的唯一约束。顺序不会经常更新:仅当管理员决定更改某些项目的显示顺序时。我更喜欢保留唯一约束以确保我的数据在数据库级别有效。谢谢!
    【解决方案2】:

    不改变约束为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 
    

    在线示例:http://rextester.com/edit/GMJ48099

    【讨论】:

    • 如果表足够大,它会生成“错误:范围表条目太多”。知道如何克服它吗?
    猜你喜欢
    • 2010-09-13
    • 2015-05-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多