DBAtlas · Atlas de operaciones de bases de datos

MERGE / UPSERT (actualizar si existe; si no, insertar)

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) compara un origen con un destino, actualiza las filas coincidentes e inserta las no coincidentes; ideal para la sincronización masiva de datos. Oracle tiene un soporte sólido de MERGE: también se pueden escribir WHEN MATCHED ... DELETE y actualizaciones condicionales. Para un origen de una sola fila se usa la tabla 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;

El upsert clásico de PostgreSQL es INSERT ... ON CONFLICT (columna única) DO UPDATE / DO NOTHING; a los nuevos valores de la fila en conflicto se accede mediante el alias EXCLUDED. Desde PostgreSQL 15 también se admite la sentencia MERGE estándar. ON CONFLICT es atómico sobre una sola tabla y muy usado.

MySQL

INSERT INTO customer (id, name, balance)
VALUES (1, 'Ali', 500)
ON DUPLICATE KEY UPDATE
  name = VALUES(name), balance = VALUES(balance);

MySQL/MariaDB usan INSERT ... ON DUPLICATE KEY UPDATE para los upserts; si hay conflicto con una clave única o primaria, la fila se actualiza. A los nuevos valores se accede con VALUES(my_column) (o con el alias de fila 'new.my_column' en 8.0.19+). INSERT IGNORE omite silenciosamente la fila en caso de conflicto. MySQL no tiene MERGE estándar.

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 admite la sentencia MERGE desde 2008; ofrece ramas adicionales como WHEN NOT MATCHED BY SOURCE para eliminar las filas sobrantes en el destino. Una sentencia MERGE debe terminar con punto y coma. Nota: debido a algunos bugs en casos límite, para upserts sencillos puede preferirse escribir sentencias INSERT/UPDATE separadas.

MongoDB

// Find-and-update or insert with upsert:true
db.customer.updateOne(
  { id: 1 },
  { $set: { name: "Ali", balance: 500 } },
  { upsert: true }
)

En MongoDB el upsert es una opción nativa: si se añade { upsert: true } a updateOne/updateMany/findOneAndUpdate, se actualiza el documento coincidente o, si no existe, se crea uno nuevo. Para la sincronización masiva, la etapa de agregación $merge (escribir/actualizar en una colección de destino) se acerca más a MERGE de SQL.

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

En Redis los comandos de escritura son upserts por naturaleza: SET/HSET actualizan la clave o el campo si existe y, si no, lo crean. Use la opción SET ... NX para escribir solo si no existe, y XX solo si existe. La distinción 'actualizar si existe; si no, insertar' ya es el comportamiento por defecto de la mayoría de los comandos.

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 }

En Elasticsearch, una llamada index (PUT) con el mismo _id reemplaza por completo el documento si existe y, si no, lo crea (similar a un upsert). Para actualizaciones parciales con inserción si no existe, use 'doc_as_upsert': true o un bloque 'upsert' en la API _update. También son posibles los upserts con script.

Abrir en DBAtlas →