ОБНОВИТЬ с 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

Необходимо обновить (увеличить) поле "последовательность". "sequence" имеют тип "index", поэтому не может быть сделано:

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

Когда "sequence" в строке с id = 10 увеличивается на 1, я получаю сообщение об ошибке, что еще одна строка с "sequence" = 2 уже существует.

Ответ 1

UPDATE с ORDER BY

Что касается вопроса, поднятого в заголовке: В команде SQL UPDATE нет ORDER BY. Postgres обновляет строки в произвольном порядке. Но у вас есть (ограниченные) варианты, чтобы решить, проверяются ли ограничения после каждой строки, после каждого оператора или в конце транзакции. Вы можете избежать повторяющихся нарушений ключа для промежуточных состояний с ограничением DEFERRABLE.

Я цитирую то, что мы разработали по этому вопросу:
Ограничение определено DEFERRABLE INITIALLY IMMEDIATE все еще откладывается?

  • Ограничения NOT DEFERRED проверяются после каждой строки.

  • Ограничения DEFERRABLE установленные на IMMEDIATE (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() в представлении или запросе. Рассмотрите этот связанный ответ:
Можно ли использовать последовательность PG на каждом лейбле?

Ответ 2

UPDATE с ORDER 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, я не думаю, что он может быть полезен для всех.

Ответ 3

Lazy Way, (ака не самый быстрый или лучший способ)

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

Это сработало для меня:

[обновить выражение здесь] ВАРИАНТ (MAXDOP 1) - предотвращает появление размера строки из-за использования требуемой катушки, которая искажает порядок обновления записей.

Я использую кластерный индекс int в последовательном порядке (генерируя его, если необходимо) и не имел проблемы до недавнего времени, и даже тогда только на небольших наборах строк, которые (в отличие от него) оптимизатор плана запросов решил использовать ленивую катушку на.

Теоретически я мог бы использовать новую опцию, чтобы запретить использование катушки, но я нахожу maxdop более простым.

Я в уникальной ситуации, потому что вычисления изолированы (один пользователь). В другой ситуации может потребоваться альтернатива использованию предела maxdop, чтобы избежать конкуренции.

Ответ 5

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;