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

Как да включите изключените редове в ВРЪЩАНЕ от INSERT ... ПРИ КОНФЛИКТ

Грешката, която получавате:

ON CONFLICT DO UPDATE командата не може да повлияе на реда втори път

показва, че се опитвате да вдигнете един и същ ред повече от веднъж в една команда. С други думи:имате измамници на (name, url, email) във вашите VALUES списък. Сгънете дубликатите (ако това е опция) и трябва да работи. Но ще трябва да решите кой ред да изберете от всеки набор от измамници.

INSERT INTO feeds_person (created, modified, name, url, email)
SELECT DISTINCT ON (name, url, email) *
FROM  (
   VALUES
   ('blah', 'blah', 'blah', 'blah', 'blah')
   -- ... more
   ) v(created, modified, name, url, email)  -- match column list
ON     CONFLICT (name, url, email) DO UPDATE
SET    url = feeds_person.url
RETURNING id;

Тъй като използваме самостоятелен VALUES израз сега, трябва да добавите изрично привеждане на типове за типове, които не са по подразбиране. Като:

VALUES
    (timestamptz '2016-03-12 02:47:56+01'
   , timestamptz '2016-03-12 02:47:56+01'
   , 'n3', 'u3', 'e3')
   ...

Вашият timestamptz колоните се нуждаят от изрично прехвърляне на тип, докато типовете низове могат да работят с text по подразбиране . (Все още можете да прехвърляте към varchar(n) веднага.)

Има начини да определите кой ред да изберете от всеки набор от измамници:

  • Изберете ли първия ред във всяка група GROUP BY?

Прав сте, няма (в момента) начин да се изключите редове в RETURNING клауза. Цитирам Postgres Wiki:

Обърнете внимание, че RETURNING не прави видимо „EXCLUDED.* " псевдоним от UPDATE (само общият "TARGET.* " псевдонимът е видим там). Смята се, че това създава досадна двусмислие за прости, често срещани случаи [30] с малка или никаква полза. В някакъв момент в бъдещето може да търсим начин да разкрием ifRETURNING -проектираните кортежи бяха вмъкнати и актуализирани, но това вероятно не е необходимо да го включва в първата ангажирана итерация на функцията [31].

Въпреки това , не трябва да актуализирате редове, които не трябва да бъдат актуализирани. Празните актуализации са почти толкова скъпи, колкото обикновените актуализации - и може да имат нежелани странични ефекти. Не се нуждаете строго от UPSERT за начало, вашият случай изглежда по-скоро като „SELECT или INSERT“. Свързано:

  • Изберете или INSERT във функция предразположени ли са условия на състезание?

Едно по-чист начин за вмъкване на набор от редове би бил с CTE, модифициращи данни:

WITH val AS (
   SELECT DISTINCT ON (name, url, email) *
   FROM  (
      VALUES 
      (timestamptz '2016-1-1 0:0+1', timestamptz '2016-1-1 0:0+1', 'n', 'u', 'e')
    , ('2016-03-12 02:47:56+01', '2016-03-12 02:47:56+01', 'n1', 'u3', 'e3')
      -- more (type cast only needed in 1st row)
      ) v(created, modified, name, url, email)
   )
, ins AS (
   INSERT INTO feeds_person (created, modified, name, url, email)
   SELECT created, modified, name, url, email FROM val
   ON     CONFLICT (name, url, email) DO NOTHING
   RETURNING id, name, url, email
   )
SELECT 'inserted' AS how, id FROM ins  -- inserted
UNION  ALL
SELECT 'selected' AS how, f.id         -- not inserted
FROM   val v
JOIN   feeds_person f USING (name, url, email);

Добавената сложност би трябвало да плати за големи таблици, където INSERT е правилото и SELECT изключението.

Първоначално бях добавил NOT EXISTS предикат на последния SELECT за да предотвратите дублиране в резултата. Но това беше излишно. Всички CTE на една заявка виждат едни и същи моментни снимки на таблици. Наборът се върна с ON CONFLICT (name, url, email) DO NOTHING се изключва взаимно за набора, върнат след INNER JOIN на същите колони.

За съжаление това също отваря малък прозорец за условия на състезание . Ако...

  • едновременна транзакция вмъква конфликтни редове
  • все още не е поел ангажимент
  • но в крайна сметка се ангажира

... някои редове може да бъдат загубени.

Може просто да INSERT .. ON CONFLICT DO NOTHING , последвано от отделен SELECT заявка за всички редове - в рамките на една и съща транзакция, за да се преодолее това. Което от своя страна отваря другмалък прозорец за състояние на състезание ако едновременните транзакции могат да извършват записи в таблицата между INSERT и SELECT (по подразбиране READ COMMITTED ниво на изолация). Може да се избегне с REPEATABLE READ изолация на транзакциите (или по-строга). Или с (вероятно скъпо или дори неприемливо) заключване на запис на цялата маса. Можете да получите всяко поведение, от което се нуждаете, но може да има цена, която трябва да платите.

Свързано:

  • Как да използвам RETURNING с ON CONFLICT в PostgreSQL?
  • Връщане на редове от INSERT с ON CONFLICT без необходимост от актуализиране



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Променете групата за защита на екземпляра на базата данни на AWS RDS

  2. 3 функции, които получават деня, месеца и годината от дата в PostgreSQL

  3. Извадете месеците от дата в PostgreSQL

  4. PostgreSQL - ГРЕШКА:датата на колоната не може да бъде прехвърлена към тип дата

  5. Имената на колоните в PostgreSQL са чувствителни към главни букви?