Несколько месяцев назад я узнал из ответа на Stack Overflow, как выполнять несколько обновлений одновременно в MySQL, используя следующий синтаксис:
INSERT INTO table (id, field, field2) VALUES (1, A, X), (2, B, Y), (3, C, Z)
ON DUPLICATE KEY UPDATE field=VALUES(Col1), field2=VALUES(Col2);
Я сейчас переключился на PostgreSQL, и, видимо, это не правильно. Это относится ко всем правильным таблицам, поэтому я предполагаю, что это вопрос использования разных ключевых слов, но я не уверен, где в документации PostgreSQL это рассматривается.
Чтобы уточнить, я хочу вставить несколько вещей, и если они уже существуют, чтобы обновить их.
sql
postgresql
upsert
sql-merge
Teifion
источник
источник
Ответы:
PostgreSQL начиная с версии 9.5 имеет синтаксис UPSERT с предложением ON CONFLICT . со следующим синтаксисом (аналогично MySQL)
Поиск в архивах почтовой группы postgresql «upsert» приводит к тому, что в руководстве вы найдете пример того, что вы, возможно, захотите сделать :
В списке рассылки хакеров , возможно, есть пример того, как сделать это массово, используя CTE в 9.1 и выше .
Посмотрите ответ a_horse_with_no_name для более ясного примера.
источник
excluded
ссылается первое решение?excluded
таблица дает вам доступ к значениям, которые вы пытались вставить в первую очередь.Предупреждение: это не безопасно, если выполняется из нескольких сеансов одновременно (см. Предостережения ниже).
Еще один умный способ сделать «UPSERT» в postgresql - это сделать два последовательных оператора UPDATE / INSERT, каждый из которых предназначен для успеха или не имеет никакого эффекта.
ОБНОВЛЕНИЕ будет успешным, если строка с «id = 3» уже существует, в противном случае это не имеет никакого эффекта.
INSERT будет успешным, только если строка с «id = 3» еще не существует.
Вы можете объединить эти два параметра в одну строку и запустить их оба с помощью одного оператора SQL, выполняемого из вашего приложения. Настоятельно рекомендуется запускать их вместе в одной транзакции.
Это работает очень хорошо, когда выполняется изолированно или на заблокированной таблице, но в зависимости от условий гонки, которые означают, что он все равно может потерпеть неудачу с ошибкой повторяющегося ключа, если строка вставлена одновременно, или может закончиться, если строка не будет вставлена, если строка удалена одновременно ,
SERIALIZABLE
Сделка по PostgreSQL 9.1 или выше будет обрабатывать его надежно за счет очень высокой интенсивности отказов сериализации, то есть вы должны будете повторить много. Посмотрите, почему так сложно , что обсуждает этот случай более подробно.Этот подход также подвержен потерям обновлений
read committed
изолированно, если только приложение не проверит число затронутых строк и не проверит, является лиinsert
илиupdate
затронутая строка .источник
... where not exists (select 1 from table where id = 3);
read committed
привести к потере обновлений изолированно, если только ваше приложение не проверит, чтобы убедиться, что уinsert
илиupdate
нет ненулевого количества строк. См. Dba.stackexchange.com/q/78510/7788В PostgreSQL 9.1 этого можно достичь с помощью записываемого CTE ( общее табличное выражение ):
Смотрите эти записи в блоге:
Обратите внимание, что это решение не предотвращает нарушение уникального ключа, но оно не уязвимо для потерянных обновлений.
Смотрите продолжение Крейг Рингер на dba.stackexchange.com
источник
UPDATE
затронуты какие-либо строки.В PostgreSQL 9.5 и новее вы можете использовать
INSERT ... ON CONFLICT UPDATE
.Смотрите документацию .
MySQL
INSERT ... ON DUPLICATE KEY UPDATE
может быть непосредственно перефразирован вON CONFLICT UPDATE
. Синтаксис стандарта SQL также не является, они оба являются специфичными для базы данных расширениями. Есть веские причины ,MERGE
не использовались для этого , новый синтаксис не был создан просто для удовольствия. (Синтаксис MySQL также имеет проблемы, которые означают, что он не был принят напрямую).например, данная настройка:
запрос MySQL:
будет выглядеть так:
Отличия:
Вы должны указать имя столбца (или уникальное имя ограничения) , чтобы использовать для единственности проверки. Это
ON CONFLICT (columnname) DO
Ключевое слово
SET
должно быть использовано, как если бы это было нормальноеUPDATE
утверждениеОн также имеет несколько приятных особенностей:
Вы можете иметь
WHERE
пункт о вашейUPDATE
( что позволяет эффективно превратитьON CONFLICT UPDATE
вON CONFLICT IGNORE
определенных значениях)Предлагаемые для вставки значения доступны в виде строковой переменной
EXCLUDED
, которая имеет ту же структуру, что и целевая таблица. Вы можете получить исходные значения в таблице, используя имя таблицы. Таким образом, в этом случаеEXCLUDED.c
будет10
(потому что это то, что мы пытались вставить) и"table".c
будет,3
потому что это текущее значение в таблице. Вы можете использовать одно или оба вSET
выражениях иWHERE
предложениях.Для справки по upsert смотрите Как сделать UPSERT (MERGE, INSERT ... ON DUPLICATE UPDATE) в PostgreSQL?
источник
ON DUPLICATE KEY UPDATE
. Я скачал Postgres 9.5 и внедрил ваш код, но, как ни странно, та же проблема возникает в Postgres: поле последовательного ключа первичного ключа не является последовательным (между вставками и обновлениями есть пробелы). Есть идеи, что здесь происходит? Это нормально? Есть идеи, как избежать такого поведения? Спасибо.SERIAL
/SEQUENCE
илиAUTO_INCREMENT
не иметь пропусков. Если вам нужны последовательности без промежутков, они более сложны; вам обычно нужно использовать таблицу счетчиков. Google скажет вам больше. Но имейте в виду, что последовательности без пробелов предотвращают параллелизм всех вставок.BEGIN ... EXCEPTION ...
выполняется в подтранзакции, которая откатывается при ошибке, приращение последовательности будет отменено в случае сбояINSERT
.Когда я пришел сюда, я искал то же самое, но отсутствие общей функции «upsert» немного беспокоило меня, поэтому я подумал, что вы можете просто пропустить обновление и вставить sql в качестве аргументов этой функции из руководства.
это будет выглядеть так:
и, возможно, чтобы сделать то, что вы изначально хотели сделать, пакетный «upsert», вы можете использовать Tcl для разделения sql_update и зацикливания отдельных обновлений, попадание preformance будет очень маленьким, см. http://archives.postgresql.org/pgsql- производительность / 2006-04 / msg00557.php
самая высокая стоимость выполнения запроса из вашего кода, на стороне базы данных стоимость выполнения намного меньше
источник
DELETE
если только вы не заблокируете таблицу или не находитесь вSERIALIZABLE
изоляции транзакций на PostgreSQL 9.1 или более поздней версии .Для этого нет простой команды.
Самый правильный подход - использовать функцию, подобную той, что есть в документации .
Другое решение (хотя и не такое уж безопасное) - выполнить обновление с возвратом, проверить, какие строки были обновлениями, и вставить остальные из них.
Что-то вроде:
при условии, что id: 2 был возвращен:
Конечно, это рано или поздно выручит (в параллельной среде), поскольку здесь есть ясное условие гонки, но обычно это будет работать.
Вот более длинная и более полная статья по теме .
источник
Лично я установил «правило», прикрепленное к оператору вставки. Скажем, у вас есть таблица «dns», в которой записано количество посещений DNS для каждого клиента в отдельности:
Вы хотели иметь возможность повторно вставлять строки с обновленными значениями или создавать их, если они еще не существовали. Введите идентификатор клиента и время. Что-то вроде этого:
Обновление: это может привести к сбою при одновременных вставках, так как это приведет к исключениям unique_violation. Однако незавершенная транзакция будет продолжена и завершится успешно, и вам просто нужно повторить прерванную транзакцию.
Тем не менее, если все время происходит множество операций вставки, вам нужно установить блокировку таблицы вокруг операторов вставки: блокировка SHARE ROW EXCLUSIVE предотвратит любые операции, которые могут вставлять, удалять или обновлять строки в вашей целевой таблице. Однако обновления, которые не обновляют уникальный ключ, безопасны, поэтому, если вы не выполняете никаких действий, используйте вместо этого рекомендательные блокировки.
Кроме того, команда COPY не использует ПРАВИЛА, поэтому, если вы вставляете с помощью COPY, вам нужно будет использовать триггеры.
источник
Я использую эту функцию слияния
источник
update
первое, а затем проверить количество обновленных строк. (См. Ответ Ахмада)Я настраивал функцию "upsert" выше, если вы хотите вставить и заменить:
`
И после выполнения сделайте что-то вроде этого:
Важно ставить двойную запятую, чтобы избежать ошибок компилятора
источник
Похож на наиболее понравившийся ответ, но работает немного быстрее:
(источник: http://www.the-art-of-web.com/sql/upsert/ )
источник
У меня та же проблема для управления настройками учетной записи, что и для пар имя-значение. Критерии дизайна таковы, что разные клиенты могут иметь разные наборы настроек.
Мое решение, аналогичное JWP, заключается в массовом удалении и замене, генерируя запись слияния в вашем приложении.
Это довольно пуленепробиваемый, независимый от платформы, и поскольку на клиента не может быть более 20 настроек, это всего лишь 3 вызова с достаточно низкой нагрузкой - вероятно, самый быстрый метод.
Альтернативой обновления отдельных строк - проверки исключений, а затем вставки - или какой-либо комбинации является отвратительный код, медленный и часто ломающийся, потому что (как упомянуто выше) нестандартная обработка исключений SQL изменяется с db на db - или даже выпуск для выпуска.
источник
REPLACE INTO
чемINSERT INTO ... ON DUPLICATE KEY UPDATE
, что может вызвать проблемы , если вы используете триггеры. В конечном итоге вы будете запускать удаление и вставлять триггеры / правила, а не обновлять их.Согласно документации
INSERT
оператора PostgreSQL , обработкаON DUPLICATE KEY
дела не поддерживается. Эта часть синтаксиса является проприетарным расширением MySQL.источник
MERGE
- это действительно больше операция OLAP; см. stackoverflow.com/q/17267417/398670 для объяснения. Он не определяет семантику параллелизма, и большинство людей, которые используют его для upsert, просто создают ошибки.источник
Для объединения небольших наборов хорошо использовать вышеуказанную функцию. Однако, если вы объединяете большие объемы данных, я бы посоветовал заглянуть в http://mbk.projects.postgresql.org
В настоящее время я знаю о наилучшей практике:
источник
UPDATE вернет количество измененных строк. Если вы используете JDBC (Java), вы можете сравнить это значение с 0 и, если строки не были затронуты, вместо этого запустить INSERT. Если вы используете какой-то другой язык программирования, возможно, число измененных строк все еще можно получить, проверьте документацию.
Это может быть не так элегантно, но у вас гораздо более простой SQL, который более тривиально использовать из вызывающего кода. Иными словами, если вы пишете сценарий из десяти строк на PL / PSQL, вам, вероятно, нужно провести модульный тест того или иного вида только для него.
источник
Изменить: это не работает, как ожидалось. В отличие от принятого ответа, это приводит к нарушениям уникального ключа, когда два процесса повторно вызывают
upsert_foo
одновременно.Эврика! Я нашел способ сделать это в одном запросе: использовать
UPDATE ... RETURNING
для проверки, были ли затронуты какие-либо строки:Это
UPDATE
должно быть сделано в отдельной процедуре, потому что, к сожалению, это синтаксическая ошибка:Теперь все работает как надо:
источник