DBAtlas · 数据库运维图谱

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;

PostgreSQL 的传统 upsert 是 INSERT ... ON CONFLICT (unique column) 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 使用 INSERT ... ON DUPLICATE KEY UPDATE 实现 upsert;如果唯一键或主键冲突,则更新该行。新值通过 VALUES(my_column)(或 8.0.19+ 中的 'new.my_column' 行别名)访问。INSERT IGNORE 在冲突时静默跳过该行。MySQL 没有标准 MERGE。

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 自 2008 起支持 MERGE 语句;它提供 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 是原生选项:在 updateOne/updateMany/findOneAndUpdate 中加上 { upsert: true },若有匹配文档则更新,否则创建新文档。对于批量同步,聚合 $merge 阶段(写入/更新到目标集合)更接近 SQL 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 中,使用相同 _id 的 index(PUT)调用会在文档存在时完全替换,否则创建(类似 upsert)。对于“不存在则插入”的部分更新,在 _update API 中使用 'doc_as_upsert': true 或 'upsert' 块。也可以使用脚本化 upsert。

在 DBAtlas 中打开 →