7种数据库的存在则更新,不存在则插入(UPSERT)SQL写法

发布时间 2023-09-10 19:06:03作者: 谢家树

在工作中遇到了这么一个需求,在往数据库插入数据时,如果一条数据不存在则插入,如果存在(根据主键或唯一索引判断)则更新,也就是所谓的UPSERT操作。大部分数据库都没有UPSERT语句,不过他们都有一些SQL的写法来实现这样的操作。

1. MySQL

使用ON DUPLICATE KEY UPDATE,如果要插入这条数据将导致唯一索引冲突或主键冲突,则会对原有数据进行UPDATE操作,否则进行INSERT操作。

假设表t1中a是主键,如:

INSERT INTO t1 (a,b,c) VALUES (1,2,3)
  ON DUPLICATE KEY UPDATE c=c+1;

UPDATE后可以接多个列,如:

INSERT INTO t1 (a,b,c) VALUES (1,2,3)
  ON DUPLICATE KEY UPDATE b=2, c=3;

对于多行数据可以这么写(采用别名):

INSERT INTO t1 (a,b,c) VALUES (1,2,3),(4,5,6) AS new
  ON DUPLICATE KEY UPDATE c = new.a+new.b;

注:如果没有主键或唯一索引,每次都会INSERT,永远不会UPDATE。
如果表中有多个唯一索引,并且有多行都匹配上了,也只会更新一行,所以不要在有多个唯一索引的表中使用ON DUPLICATE KEY UPDATE。

2. Oracle

使用MERGE INTO,根据给定的列的值来决定INSERT还是UPDATE,用于判断的列可以不是主键。

MERGE INTO ALERT_INFO
USING DUAL
ON (ALERT_INFO.ID = '7')
WHEN NOT MATCHED
    THEN
    INSERT (ID,CONTENT) VALUES ('7','A7')
WHEN MATCHED
    THEN
    UPDATE SET CONTENT = 'A7';


MERGE INTO ALERT_INFO
USING DUAL
ON (ALERT_INFO.CONTENT = 'AA6')
WHEN NOT MATCHED
    THEN
    INSERT (ID,CONTENT) VALUES ('6','AA5')
WHEN MATCHED
    THEN
    UPDATE SET ID = '6';

3. SqlServer

使用MERGE INTO,根据给定的列的值来决定INSERT还是UPDATE,用于判断的列可以不是主键。
注:官方文档说这种用法可能带来并发问题。

MERGE INTO xh.dbo.hhhh AS tgt  
USING (VALUES ('Recommendation222',111), ('Review222', 211),
              ('Internet', 311))  
       as src (NewName, NewReasonType)  
ON tgt.aaa = src.NewName  
WHEN MATCHED THEN  
UPDATE SET bbb = src.NewReasonType  
WHEN NOT MATCHED BY TARGET THEN  
INSERT (aaa, bbb) VALUES (NewName, NewReasonType);

4. PostgreSQL

指定主键/唯一索引的列决定INSERT还是UPDATE
注:官方文档说这种用法是原子的
假设在public这个schema下的表student中,id是主键

INSERT INTO public.student (id, name, description)
    VALUES (5, 'Gizmo Transglobal', 'xxx'), (6, 'Associated Computing, Inc', 'xxxxx')
    ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name, description=EXCLUDED.description;

5. 达梦(DM)

使用MERGE INTO,根据给定的列的值来决定INSERT还是UPDATE,用于判断的列可以不是主键。
与Oracle语法相同

MERGE INTO ALERT_INFO
USING DUAL
ON (ALERT_INFO.ID = '1')
WHEN NOT MATCHED
    THEN
    INSERT (ID,CONTENT) VALUES ('1','A1')
WHEN MATCHED
    THEN
    UPDATE SET CONTENT = 'A1';

MERGE INTO ALERT_INFO
USING DUAL
ON (ALERT_INFO.CONTENT = 'AA6')
WHEN NOT MATCHED
    THEN
    INSERT (ID,CONTENT) VALUES ('6','AA5')
WHEN MATCHED
    THEN
    UPDATE SET ID = '6';

6. 人大金仓(KingBase)

使用MERGE INTO,根据给定的列的值来决定INSERT还是UPDATE,用于判断的列可以不是主键。
与Oracle语法相同

MERGE INTO ALERT_INFO
USING DUAL
ON (ALERT_INFO.ID = '1')
WHEN NOT MATCHED
    THEN
    INSERT (ID,CONTENT) VALUES ('1','A1')
WHEN MATCHED
    THEN
    UPDATE SET CONTENT = 'A1';

MERGE INTO ALERT_INFO
USING DUAL
ON (ALERT_INFO.CONTENT = 'AA6')
WHEN NOT MATCHED
    THEN
    INSERT (ID,CONTENT) VALUES ('6','AA5')
WHEN MATCHED
    THEN
    UPDATE SET ID = '6';

7. 南大通用(GBase8s)

使用MERGE INTO,根据给定的列的值来决定INSERT还是UPDATE,用于判断的列可以不是主键。
与Oracle语法相同

MERGE INTO ALERT_INFO
USING DUAL
ON (ALERT_INFO.ID = '1')
WHEN NOT MATCHED
    THEN
    INSERT (ID,CONTENT) VALUES ('1','A1')
WHEN MATCHED
    THEN
    UPDATE SET CONTENT = 'A1';

MERGE INTO ALERT_INFO
USING DUAL
ON (ALERT_INFO.CONTENT = 'AA6')
WHEN NOT MATCHED
    THEN
    INSERT (ID,CONTENT) VALUES ('6','AA5')
WHEN MATCHED
    THEN
    UPDATE SET ID = '6';

更详细的介绍请进入参考资料中的官方文档查看

参考资料:

MySQL官方文档:
https://dev.mysql.com/doc/refman/8.0/en/insert-on-duplicate.html
Oracle官方文档:
https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/MERGE.html#GUID-5692CCB7-24D9-4C0E-81A7-A22436DC968F
SqlServer官方文档:
https://learn.microsoft.com/zh-cn/sql/t-sql/statements/merge-transact-sql?view=sql-server-ver16
PostgreSQL官方文档:
https://www.postgresql.org/docs/15/sql-insert.html
达梦官方文档:
https://eco.dameng.com/document/dm/zh-cn/sql-dev/practice-dml-operation.html
人大金仓官方文档:
https://help.kingbase.com.cn/v8/development/sql-plsql/sql/SQL_Statements_10.html#merge
南大通用官方文档:
https://cdn.gbase.cn/products/27/b--vfeeESpHTsMJy2SZ0Y-GBase 8s V8.8 SQL 指南:语法.pdf