PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

Как да UPSERT (СЛИВАНЕ, ВМЕСВАНЕ ... ПРИ ДУБЛИРАНЕ НА АКТУАЛИЗИРАНЕ) в PostgreSQL?

9.5 и по-нови:

PostgreSQL 9.5 и по-нова поддръжка INSERT ... ON CONFLICT (key) DO UPDATEON CONFLICT (key) DO NOTHING ), т.е. нагоре.

Сравнение с ON DUPLICATE KEY UPDATE .

Бързо обяснение.

За употреба вижте ръководството - по-специално conflict_action клауза в синтактичната диаграма и обяснителния текст.

За разлика от решенията за 9.4 и по-стари, които са дадени по-долу, тази функция работи с множество конфликтни реда и не изисква изключително заключване или цикъл за повторен опит.

Комитът за добавяне на функцията е тук, а дискусията около нейното развитие е тук.

Ако сте на 9.5 и не е необходимо да имате обратна съвместимост, можете да спрете да четете сега .

9.4 и по-стари:

PostgreSQL няма вграден UPSERT (или MERGE ) съоръжение и е много трудно да го направите ефективно в условията на едновременна употреба.

Тази статия разглежда проблема с полезни подробности.

По принцип трябва да избирате между две опции:

  • Отделни операции за вмъкване/актуализация в цикъл за повторен опит; или
  • Заключване на таблицата и извършване на групово сливане

Цикъл за повторен опит за отделен ред

Използването на отделни прехвърляния на ред в цикъл за повторен опит е разумната опция, ако искате много връзки едновременно да се опитват да извършват вмъквания.

Документацията на PostgreSQL съдържа полезна процедура, която ще ви позволи да направите това в цикъл в базата данни. Той предпазва от загубени актуализации и състезания за вмъкване, за разлика от повечето наивни решения. Ще работи само в READ COMMITTED режим и е безопасен само ако това е единственото нещо, което правите в транзакцията. Функцията няма да работи правилно, ако тригери или вторични уникални ключове причинят уникални нарушения.

Тази стратегия е много неефективна. Когато е практично, трябва да наредите работа на опашка и вместо това да извършите групово прехвърляне, както е описано по-долу.

Много опитни решения на този проблем не отчитат връщане назад, така че водят до непълни актуализации. Две транзакции се състезават една с друга; един от тях успешно INSERT с; другият получава грешка с дублиран ключ и прави UPDATE вместо. UPDATE блокове, чакащи INSERT за връщане назад или ангажиране. Когато се върне назад, UPDATE повторната проверка на условието съответства на нула редове, така че въпреки че UPDATE обвързва, че всъщност не е направило разстройството, което сте очаквали. Трябва да проверите броя на редовете с резултати и да опитате отново, където е необходимо.

Някои опитни решения също не успяват да вземат предвид състезанията SELECT. Ако опитате очевидното и просто:

-- THIS IS WRONG. DO NOT COPY IT. It's an EXAMPLE.

BEGIN;

UPDATE testtable
SET somedata = 'blah'
WHERE id = 2;

-- Remember, this is WRONG. Do NOT COPY IT.

INSERT INTO testtable (id, somedata)
SELECT 2, 'blah'
WHERE NOT EXISTS (SELECT 1 FROM testtable WHERE testtable.id = 2);

COMMIT;

тогава когато двама работят наведнъж, има няколко режима на отказ. Единият е вече обсъжданият проблем с повторна проверка на актуализацията. Друго е, където и двете UPDATE в същото време, съвпадение на нула редове и продължаване. След това и двамата правят EXISTS тест, който се случва преди INSERT . И двете получават нулеви редове, така че и двете правят INSERT . Единият се проваля с грешка с дублиран ключ.

Ето защо имате нужда от цикъл за повторен опит. Може да си мислите, че можете да предотвратите дублиращи се ключови грешки или загубени актуализации с интелигентен SQL, но не можете. Трябва да проверите броя на редовете или да се справите с дублиращи се ключови грешки (в зависимост от избрания подход) и да опитате отново.

Моля, не предлагайте собствено решение за това. Както при опашката за съобщения, вероятно е погрешно.

Насипно прехвърляне със заключване

Понякога искате да направите групово прехвърляне, при което имате нов набор от данни, който искате да обедините в по-стар съществуващ набор от данни. Това е изключително по-ефективен от преместването на отделни редове и трябва да се предпочита, когато е практично.

В този случай обикновено следвате следния процес:

  • CREATE a TEMPORARY таблица

  • COPY или групово вмъкнете новите данни във временната таблица

  • LOCK целевата таблица IN EXCLUSIVE MODE . Това позволява на други транзакции да SELECT , но не правете никакви промени в таблицата.

  • Направете UPDATE ... FROM на съществуващи записи, използващи стойностите във временната таблица;

  • Направете INSERT от редове, които все още не съществуват в целевата таблица;

  • COMMIT , освобождаване на ключалката.

Например, за примера, даден във въпроса, използвайки многозначно INSERT за да попълните временната таблица:

BEGIN;

CREATE TEMPORARY TABLE newvals(id integer, somedata text);

INSERT INTO newvals(id, somedata) VALUES (2, 'Joe'), (3, 'Alan');

LOCK TABLE testtable IN EXCLUSIVE MODE;

UPDATE testtable
SET somedata = newvals.somedata
FROM newvals
WHERE newvals.id = testtable.id;

INSERT INTO testtable
SELECT newvals.id, newvals.somedata
FROM newvals
LEFT OUTER JOIN testtable ON (testtable.id = newvals.id)
WHERE testtable.id IS NULL;

COMMIT;

Свързано четене

  • UPSERT уики страница
  • UPSERTизми в Postgres
  • Вмъкване при дублирана актуализация в PostgreSQL?
  • http://petereisentraut.blogspot.com/2010/05/merge-syntax.html
  • Направете с транзакция
  • Изберете или INSERT във функция предразположени ли са условия на състезание?
  • SQL MERGE в уикито на PostgreSQL
  • Най-идиоматичният начин за внедряване на UPSERT в Postgresql в днешно време

Ами MERGE ?

SQL-стандарт MERGE всъщност има лошо дефинирана семантика на паралелност и не е подходящ за добавяне без първо заключване на таблица.

Това е наистина полезен оператор на OLAP за обединяване на данни, но всъщност не е полезно решение за безопасно прехвърляне на паралелност. Има много съвети към хората, които използват други СУБД, да използват MERGE за разстройства, но всъщност е погрешно.

Други БД:

  • INSERT ... ON DUPLICATE KEY UPDATE в MySQL
  • MERGE от MS SQL Server (но вижте по-горе за MERGE проблеми)
  • MERGE от Oracle (но вижте по-горе за MERGE проблеми)


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. date_trunc 5 минути интервал в PostgreSQL

  2. Изображението на Psycopg2 не е намерено

  3. Функция за динамична заявка на Postgres

  4. Как да се свържете с Postgres чрез Node.js

  5. Как да получите текущата дата в PostgreSQL