Увеличивая одним запросом набор значений в поле с ограничением UNIQUE, Postgres - PullRequest
8 голосов
/ 19 марта 2011

У меня есть таблица, в которой у меня есть числовое поле A , которое установлено в UNIQUE . Это поле используется для указания порядка, в котором должно быть выполнено какое-либо действие. Я хочу сделать ОБНОВЛЕНИЕ всех значений, которые, например, больше, чем 3 . Например, У меня есть

A     
1
2
3
4
5

Теперь я хочу добавить 1 ко всем значениям A , превышающим 3 . Таким образом, результат будет

A     
1
2
3
5
6

Вопрос в том, возможно ли это сделать, используя только один запрос? Помните, что у меня есть ограничение UNIQUE на столбец A .

Очевидно, я пытался

UPDATE my_table SET A = A + 1 WHERE A > 3;

но это не сработало, так как у меня есть ограничение на это поле.

Ответы [ 3 ]

10 голосов
/ 19 марта 2011

PostgreSQL 9.0 и более поздние версии

В PostgreSQL 9.0 добавлено отложенные уникальные ограничения , что является именно той функцией, которая вам, кажется, нужна.Таким образом, уникальность проверяется во время фиксации, а не во время обновления.

Создайте ограничение UNIQUE с ключевым словом DEFERRABLE:

ALTER TABLE foo ADD CONSTRAINT foo_uniq (foo_id) DEFERRABLE;

Позже, перед выполнением оператора UPDATE, вы запускаетев той же транзакции:

SET CONSTRAINTS foo_uniq DEFERRED;

В качестве альтернативы вы можете создать ограничение с ключевым словом INITIALLY DEFERRED для самого уникального ограничения, поэтому вам не нужно запускать SET CONSTRAINTS - но это может повлиять напроизводительность других ваших запросов, которым не нужно откладывать ограничение.

PostgreSQL 8.4 и старше

Если вы хотите использовать уникальное ограничение только для гарантии уникальности - не в качестве целидля внешнего ключа - тогда этот обходной путь может помочь:

Сначала добавьте в таблицу логический столбец, например is_temporary, который временно различает обновленные и не обновленные строки:

CREATE TABLE foo (value int not null, is_temporary bool not null default false);

Затем создайте частичный уникальный индекс , который влияет только на строки, где is_teven = false:

CREATE UNIQUE INDEX ON foo (value) WHERE is_temporary=false;

Теперь каждый раз, когда вы делаете обновления, которые вы удалилиВ действительности, вы запускаете их в два этапа:

UPDATE foo SET is_temporary=true, value=value+1 WHERE value>3;
UPDATE foo SET is_temporary=false WHERE is_temporary=true;

Пока эти операторы выполняются в одной транзакции, это будет абсолютно безопасно - другие сеансы никогда не будут видеть временные строки.Недостатком является то, что вы будете писать строки дважды.

Обратите внимание, что это просто уникальный индекс, а не ограничение , но на практике это не должно иметь значения.

2 голосов
/ 24 февраля 2017

Вы можете сделать это в 2 запроса с помощью простого трюка:

Сначала обновите ваш столбец с +1, но добавьте коэффициент с топором (-1):

update my_table set A=(A+1)*-1  where A > 3.

Вы получите от 4,5,6 до -5, -6, -7

Во-вторых, конвертируйте обратно операцию для восстановления положительного значения:

update my_table set A=(A)*-1  where A < 0.

У вас будет: 5,6, 7

0 голосов
/ 19 марта 2011

Вы можете сделать это с помощью цикла.Мне не нравится это решение, но оно работает:

CREATE TABLE update_unique (id INT NOT NULL);
CREATE UNIQUE INDEX ux_id ON update_unique (id);
INSERT INTO update_unique(id) SELECT a FROM generate_series(1,100) AS foo(a);

DO $$
DECLARE v INT;
BEGIN
  FOR v IN SELECT id FROM update_unique WHERE id > 3 ORDER BY id DESC 
  LOOP
    UPDATE update_unique SET id = id + 1 WHERE id = v;
  END LOOP;
END;
$$ LANGUAGE 'plpgsql';

В PostgreSQL 8.4 вам может потребоваться создать функцию для этого, поскольку вы не можете произвольно запустить PL / PGSQL из приглашения, используя DO(по крайней мере, насколько я помню).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...