使用ORDER BY更新

时间:2013-05-24 13:19:04

标签: sql postgresql sql-update sql-order-by

需要与UPDATE“绑定”ORDER BY。我正在尝试使用游标,但得到错误:

cursor "cursupd" doesn't specify a line,
SQL state: 24000

代码:

BEGIN;
    DECLARE cursUpd CURSOR FOR SELECT * FROM "table" WHERE "field" = 5760 AND "sequence" >= 0 AND "sequence" < 9 ORDER BY "sequence" DESC;
    UPDATE "table" SET "sequence" = "sequence" + 2 WHERE CURRENT OF cursUpd;
    CLOSE cursUpd;
COMMIT;

如何正确地做到这一点?

更新1

没有光标,当我这样做时:

UPDATE "CableLinePoint" AS "t"
SET "sequence" = t."sequence" + 2
from (
    select max("sequence") "sequence", "id"
    from "CableLinePoint"
    where
        "CableLine" = 5760
    group by "id"
    ORDER BY "sequence" DESC
) "s"
where "t"."id" = "s"."id" and "t"."sequence" = "s"."sequence"

我得到了唯一的错误。因此,需要从最终而不是从头开始更新。

更新2

表:

id|CableLine|sequence
10|    2    |    1
11|    2    |    2
12|    2    |    3
13|    2    |    4
14|    2    |    5

需要更新(增加)字段“序列”。 “序列”具有“索引”类型,因此无法完成:

UPDATE "table" SET "sequence" = "sequence" + 1 WHERE "CableLine" = 2

id = 10行中的“序列”增加1时,我收到一条错误,指出已存在"sequence" = 2的另一行。

6 个答案:

答案 0 :(得分:12)

UPDATEORDER BY

UPDATE thetable 
  SET columntoupdate=yourvalue 
 FROM (SELECT rowid, 'thevalue' AS yourvalue 
         FROM thetable 
        ORDER BY rowid
      ) AS t1 
WHERE thetable.rowid=t1.rowid;

UPDATE顺序仍然是随机的(我猜),但提供给UPDATE命令的值与thetable.rowid=t1.rowid条件匹配。所以我要做的是,首先选择内存中的“更新”表,在上面的代码中将其命名为t1,然后使我的物理表看起来与t1相同。更新顺序不再重要。

对于真正有序的UPDATE,我认为它对任何人都没有用。

答案 1 :(得分:10)

带有UPDATE

ORDER BY

关于提出的问题标题:SQL ORDER BY命令中没有UPDATE。 Postgres以任意顺序更新行。但是您有(有限的)选项来决定是在每行之后,每个语句之后还是在事务结束时检查约束。您可以避免对具有DEFERRABLE约束的中间状态的重复键冲突。

我引用了我们在这个问题下制定的内容:
Constraint defined DEFERRABLE INITIALLY IMMEDIATE is still DEFERRED?

  • NOT DEFERRED约束在每行后检查

  • 设置为DEFERRABLEIMMEDIATE或通过INITIALLY IMMEDIATE)的
  • SET CONSTRAINTS约束在每个声明后检查

但是有一些限制。外键约束要求目标列上的不可延迟的约束。

  

引用的列必须是不可延迟的唯一列   或引用表中的主键约束。

解决方法

更新问题后更新。
假设"sequence"在正常操作中从不为负,您可以避免出现如下所示的唯一错误:

UPDATE tbl SET "sequence" = ("sequence" + 1) * -1
WHERE  "CableLine" = 2;

UPDATE tbl SET "sequence" = "sequence" * -1
WHERE  "CableLine" = 2
AND    "sequence" < 0;

使用不可延迟的约束(默认),您必须运行两个单独的事务才能使其工作。快速连续运行命令以避免并发问题。该解决方案显然不适合重载并发负载。

除了:
跳过表别名的关键字AS是可以的,但是不建议对列别名执行相同的操作。

我建议不要使用SQL关键字作为标识符,即使这是允许的。

避免问题

在更大规模或具有大量并发负载的数据库中,使用serial列进行行的相对排序更明智。您可以在视图或查询中生成以1开头且没有窗口函数row_number()的间隙的数字。考虑这个相关的答案:
Is it possible to use a PG sequence on a per record label?

答案 2 :(得分:1)

Update with Order By
Declare 
v number;
cursor c1 is 
    Select col2 from table1 order by col2;
    begin
    v:=0;
     for c in c1
     loop
    update table1 
    set col1 =v+1
    where col2 = c.col2;
    end loop;
    commit;
    END;

答案 3 :(得分:0)

懒惰的方式,(又名不是最快或最好的方式)

CREATE OR REPLACE FUNCTION row_number(table_name text, update_column text, start_value integer, offset_value integer, order_by_column text, order_by_descending boolean)
  RETURNS void AS
$BODY$
DECLARE
    total_value integer;
    my_id text;
    command text;
BEGIN
total_value = start_value;
    command = 'SELECT ' || order_by_column || ' FROM ' || table_name || ' ORDER BY '  || order_by_column;

    if (order_by_descending) THEN
        command = command || ' desc';
    END IF;

    FOR  my_id in  EXECUTE command LOOP
        command = 'UPDATE ' || table_name || ' SET  ' || update_column || ' = ' || total_value || ' WHERE ' || order_by_column || ' = ' ||  my_id|| ';';

        EXECUTE command;
        total_value = total_value + offset_value;
    END LOOP;
END;
$BODY$
  LANGUAGE 'plpgsql' VOLATILE
  COST 100;

实施例

SELECT row_number('regispro_spatial_2010.ags_states_spatial','order_id',10,1,'ogc_fid',true)

答案 4 :(得分:0)

这对我有用:

[此处更新声明] 选项(MAXDOP 1) - 防止行大小导致使用急切线轴,这会破坏记录更新的顺序。

我按顺序使用聚簇int索引(如果需要,生成一个)并且直到最近才出现问题,即使这样,只有在小行集上(反直觉地)查询计划优化器决定使用惰性假脱机

理论上我可以使用新选项来禁止使用假脱机,但我发现maxdop更简单。

我处于一种独特的情况,因为计算是孤立的(单个用户)。不同的情况可能需要使用maxdop限制来替代争用。

答案 5 :(得分:0)

如果有人来这里,就像我来解决从1重新排列postgresql table_id_seq并按ID排序的问题一样。我尝试的解决方案部分来自@Syd Nazam Ul Hasan(上方)和https://gist.github.com/JoshCheek/e19f83f271dc16d7825e2e4079538ba8

CREATE OR REPLACE FUNCTION update_sequence()
RETURNS SETOF varchar AS $$
DECLARE
  curs CURSOR FOR SELECT * FROM table ORDER BY id ASC;
  row  RECORD;
  v INTEGER := 0;
BEGIN
  open curs;
  LOOP
    FETCH FROM curs INTO row;
    update table 
    set id = v+1
    where id = row.id;
    v = v+1;
    EXIT WHEN NOT FOUND;
    return next row.id;
  END LOOP;
END; $$ LANGUAGE plpgsql;

SELECT update_sequence();