SQL DML合并数据

SQL:2003 标准中引入了一个新的数据操作语句:MERGE。MERGE 可以同时完成 INSERT 和 UPDATE 的操作,甚至 DELETE 的功能

目前只有 Oracle 和SQL Server 支持 MERGE 语句,简单的 MERGE 语法如下:

-- Oracle 和 SQL Server 实现
MEGRE INTO target_table [AS t_alias] 
USING source_table [AS s_alias]
   ON (condition)
 WHEN MATCHED THEN
      UPDATE SET column1 = expr_1,
                 column2 = expr_2,
                 ...
 WHEN NOT MATCHED THEN
      INSERT (column1, column2, ...)
      VALUES (expr_1, expr_2, ...);

其中,

  • target_table 是合并操作的目标表;
  • USING 指定了数据的来源,可以是一个表或者查询结果;
  • ON 指定了合并的条件,通常是主键或者唯一键相等;
  • 对于数据源中的每一行,如果在目标表中存在满足条件的记录,执行 WHEN MATCHED THEN 分支更新目标表中对应的记录;如果不存在匹配的记录,执行 WHEN NOT MATCHED THEN 分支在目标表中插入一条新记录。

我们先创建一个用于数据合并的目标表 emp_merge:

CREATE TABLE emp_merge
    ( emp_id   INTEGER NOT NULL PRIMARY KEY
    , emp_name VARCHAR(50) NOT NULL
    , sex      VARCHAR(10) NOT NULL
    , salary   NUMERIC(8,2) NOT NULL
    , email    VARCHAR(100) NOT NULL
    );

以下示例使用 MERGE 语句将员工表中女性员工的信息合并到 emp_merge 表中:

-- Oracle 和 SQL Server 实现
MERGE INTO emp_merge t
USING (SELECT emp_id, emp_name, sex, salary, email
         FROM employee
        WHERE sex = '女') s
   ON (t.emp_id = s.emp_id)
 WHEN MATCHED THEN
      UPDATE
         SET t.emp_name = s.emp_name,
             t.sex      = s.sex,
             t.salary   = s.salary,
             t.email    = s.email
 WHEN NOT MATCHED THEN
      INSERT (emp_id, emp_name, sex, salary, email)
      VALUES (s.emp_id, s.emp_name, s.sex, s.salary, s.email);

其中,合并的条件为 emp_id 是否相同;相同则更新已有的数据,不同则插入数据。第一次运行以上语句时,由于 emp_merge 表中没有任何数据,因此执行的都是 WHEN NOT MATCHED THEN 分支,即插入新的数据。

SELECT *
  FROM emp_merge;

EMP_ID|EMP_NAME|SEX|SALARY|EMAIL                   |
------|--------|---|------|------------------------|
     7|孙尚香   |女  | 12000|sunshangxiang@shuguo.com|
     8|孙丫鬟   |女  |  6000|sunyahuan@shuguo.com    |
    12|赵氏     |女  |  6600|zhaoshi@shuguo.com      |

大家可以尝试修改 emp_merge 中的某些数据,然后再次运行上面的 MERGE 语句,看看 WHEN MATCHED THEN 分支的更新结果。

MERGE 语句也可以只包含更新或者插入操作,例如:

-- Oracle 和 SQL Server 实现
MERGE INTO emp_merge t
USING (SELECT emp_id, emp_name, sex, salary, email
         FROM employee
        WHERE sex = '女') s
   ON (t.emp_id = s.emp_id)
 WHEN NOT MATCHED THEN
      INSERT (emp_id, emp_name, sex, salary, email)
      VALUES (s.emp_id, s.emp_name, s.sex, s.salary, s.email);

以上语句只在没有匹配数据时插入新的数据,而不执行更新操作。

另外,MERGE 还支持 DELETE 功能,用于在目标表中找到匹配的数据时删除记录:

-- Oracle 实现
MERGE INTO emp_merge t
USING (SELECT emp_id, emp_name, sex, salary, email
         FROM employee
        WHERE sex = '女') s
   ON (t.emp_id = s.emp_id)
 WHEN MATCHED THEN
      UPDATE
         SET t.emp_name = s.emp_name,
             t.sex      = s.sex,
             t.salary   = s.salary,
             t.email    = s.email
      DELETE WHERE t.emp_name = '赵氏'
 WHEN NOT MATCHED THEN
      INSERT (emp_id, emp_name, sex, salary, email)
      VALUES (s.emp_id, s.emp_name, s.sex, s.salary, s.email);

-- SQL Server 实现
MERGE INTO emp_merge t
USING (SELECT emp_id, emp_name, sex, salary, email
         FROM employee
        WHERE sex = '女') s
   ON (t.emp_id = s.emp_id)
 WHEN MATCHED AND t.emp_name = '赵氏' THEN 
      DELETE
 WHEN MATCHED THEN
      UPDATE
         SET t.emp_name = s.emp_name,
             t.sex      = s.sex,
             t.salary   = s.salary,
             t.email    = s.email
 WHEN NOT MATCHED THEN
      INSERT (emp_id, emp_name, sex, salary, email)
      VALUES (s.emp_id, s.emp_name, s.sex, s.salary, s.email);

Oracle 和 SQL Server 对于 DELETE 的实现略有不同,但都是在 MATCHED 分支增加了一个删除操作。如果运行以上语句,emp_merge 表中姓名为“赵氏”的数据将会被删除。

MySQL 和 PostgreSQL 没有提供 MERGE 语句,而是使用专有的语法格式:

-- MySQL 实现
INSERT INTO emp_merge(emp_id, emp_name, sex, salary, email)
SELECT emp_id, emp_name, sex, salary, email
  FROM employee s
 WHERE sex = '女'
    ON DUPLICATE KEY UPDATE
       emp_name = s.emp_name,
       sex      = s.sex,
       salary   = s.salary,
       email    = s.email;

-- PostgreSQL 实现
INSERT INTO emp_merge(emp_id, emp_name, sex, salary, email)
SELECT emp_id, emp_name, sex, salary, email
  FROM employee
 WHERE sex = '女'
    ON CONFLICT(emp_id) DO UPDATE
   SET emp_name = EXCLUDED.emp_name,
       sex      = EXCLUDED.sex,
       salary   = EXCLUDED.salary,
       email    = EXCLUDED.email;

MySQL 中 ON DUPLICATE KEY UPDATE 表示主键或者唯一索引出现重复值时执行更新操作;PostgreSQL 中 ON CONFLICT(emp_id) DO UPDATE 表示 emp_id 重复时执行更新操作,EXCLUDED 代表了此时的源数据。这两种语法不支持 DELETE 操作。

Camera课程

Python教程

Java教程

Web教程

数据库教程

图形图像教程

办公软件教程

Linux教程

计算机教程

大数据教程

开发工具教程