MERGE / UPSERT (обновить, если есть, иначе вставить)
Oracle
MERGE INTO customer m
USING (SELECT 1 AS id, 'Ali' AS name, 500 AS balance FROM dual) k
ON (m.id = k.id)
WHEN MATCHED THEN
UPDATE SET m.name = k.name, m.balance = k.balance
WHEN NOT MATCHED THEN
INSERT (id, name, balance) VALUES (k.id, k.name, k.balance);MERGE (upsert) сопоставляет источник с целью, обновляя совпадающие строки и вставляя несовпадающие; идеален для массовой синхронизации данных. В Oracle сильная поддержка MERGE: можно писать также WHEN MATCHED ... DELETE и условные обновления. Для источника из одной строки используется таблица dual.
PostgreSQL
-- The PostgreSQL way: INSERT ... ON CONFLICT
INSERT INTO customer (id, name, balance)
VALUES (1, 'Ali', 500)
ON CONFLICT (id) DO UPDATE
SET name = EXCLUDED.name, balance = EXCLUDED.balance;Классический upsert в PostgreSQL — INSERT ... ON CONFLICT (уникальный столбец) DO UPDATE / DO NOTHING; новые значения конфликтующей строки доступны через псевдоним EXCLUDED. С PostgreSQL 15 поддерживается и стандартный оператор MERGE. ON CONFLICT атомарен на одной таблице и широко используется.
MySQL
INSERT INTO customer (id, name, balance)
VALUES (1, 'Ali', 500)
ON DUPLICATE KEY UPDATE
name = VALUES(name), balance = VALUES(balance);MySQL/MariaDB используют для upsert INSERT ... ON DUPLICATE KEY UPDATE; при конфликте уникального или первичного ключа строка обновляется. Новые значения доступны через VALUES(my_column) (или псевдоним строки 'new.my_column' в 8.0.19+). INSERT IGNORE молча пропускает строку при конфликте. Стандартного MERGE в MySQL нет.
SQL Server
MERGE INTO customer AS m
USING (VALUES (1, 'Ali', 500)) AS k(id, name, balance)
ON m.id = k.id
WHEN MATCHED THEN
UPDATE SET m.name = k.name, m.balance = k.balance
WHEN NOT MATCHED THEN
INSERT (id, name, balance) VALUES (k.id, k.name, k.balance);SQL Server поддерживает оператор MERGE с 2008; он предлагает дополнительные ветви, например WHEN NOT MATCHED BY SOURCE для удаления лишних строк в цели. Оператор MERGE должен завершаться точкой с запятой. Примечание: из-за некоторых ошибок в граничных случаях для простых upsert могут быть предпочтительны отдельные операторы INSERT/UPDATE.
MongoDB
// Find-and-update or insert with upsert:true
db.customer.updateOne(
{ id: 1 },
{ $set: { name: "Ali", balance: 500 } },
{ upsert: true }
)В MongoDB upsert — нативная опция: если добавить { upsert: true } к updateOne/updateMany/findOneAndUpdate, совпадающий документ обновляется, иначе создаётся новый. Для массовой синхронизации ближе к SQL MERGE этап агрегации $merge (запись/обновление в целевую коллекцию).
Redis
# SET is a natural upsert: it overwrites the key if present, otherwise creates it.
SET customer:1 '{"name":"Ali","balance":500}'
# Update/add a hash field:
HSET customer:1 name Ali balance 500В Redis команды записи по своей природе являются upsert: SET/HSET обновляют ключ или поле, если они есть, иначе создают. Опция SET ... NX — запись, только если ключа нет, XX — только если он есть. Различие «обновить, если есть, иначе вставить» уже является поведением большинства команд по умолчанию.
Elasticsearch
# An index call with a document id acts like an upsert (replaces if present):
PUT customer/_doc/1
{ "name": "Ali", "balance": 500 }
# Partial update + insert if absent:
POST customer/_update/1
{ "doc": { "balance": 500 }, "doc_as_upsert": true }В Elasticsearch вызов index (PUT) с тем же _id полностью заменяет документ, если он есть, иначе создаёт его (аналог upsert). Для частичных обновлений со вставкой при отсутствии используйте 'doc_as_upsert': true или блок 'upsert' в API _update. Возможны и скриптовые upsert.