迁移 Oracle 到 PostgreSQL: MERGE

七月 26, 2023

MERGE语句提供了一种指定单个 SQL 语句的方法,这些语句有条件地对目标表执行INSERTUPDATEDELETE操作,否则该任务需要多个逻辑语句。

Oracle 用法

MERGE语句从源表中选择记录,然后通过指定逻辑结构自动对目标表执行多个 DML 操作。它的主要优点是有助于避免使用多个插入、更新或删除。需要重点注意的是,MERGE是一个确定性语句。也就是说,一旦行由 MERGE 语句处理,就不能使用相同的MERGE语句再次处理它。MERGE有时也称为UPSERT

例子

使用MERGE插入或更新有权获得奖金的员工(按年)。

CREATE TABLE EMP_BONUS(
  EMPLOYEE_ID NUMERIC,
  BONUS_YEAR VARCHAR2(4),
  SALARY NUMERIC,
  BONUS NUMERIC,
  PRIMARY KEY (EMPLOYEE_ID, BONUS_YEAR)
);

MERGE INTO EMP_BONUS E1
  USING (
    SELECT EMPLOYEE_ID, FIRST_NAME, SALARY, DEPARTMENT_ID
      FROM EMPLOYEES) E2
  ON (E1.EMPLOYEE_ID = E2.EMPLOYEE_ID)
  WHEN MATCHED THEN
    UPDATE SET E1.BONUS = E2.SALARY * 0.5
    DELETE WHERE (E1.SALARY >= 10000)
  WHEN NOT MATCHED THEN
    INSERT (E1.EMPLOYEE_ID, E1.BONUS_YEAR, E1.SALARY, E1.BONUS)
      VALUES (E2.EMPLOYEE_ID,
              EXTRACT(YEAR FROM SYSDATE),
              E2.SALARY,
              E2.SALARY * 0.5)
    WHERE (E2.SALARY < 10000);

SELECT * FROM EMP_BONUS;
EMPLOYEE_ID BONUS_YEAR SALARY BONUS
103         2017       9000   4500
104         2017       6000   3000
105         2017       4800   2400
106         2017       4800   2400
107         2017       4200   2100
109         2017       9000   4500
110         2017       8200   4100
111         2017       7700   3850
112         2017       7800   3900
113         2017       6900   3450
115         2017       3100   1550

有关详细信息,请参阅 Oracle 文档中的 MERGE

PostgreSQL 用法

PostgreSQL 不支持使用 SQL 命令MERGE。作为替代方法,请考虑使用INSERT… ON CONFLICT语句,该语句可以处理插入子句可能导致冲突的情况,然后将操作重定向为更新。

例子

使用ON CONFLICT子句处理类似的场景,如 Oracle MERGE命令所示。

CREATE TABLE EMP_BONUS (
  EMPLOYEE_ID NUMERIC,
  BONUS_YEAR VARCHAR(4),
  SALARY NUMERIC,
  BONUS NUMERIC,
  PRIMARY KEY (EMPLOYEE_ID, BONUS_YEAR)
);

INSERT INTO EMP_BONUS
  (EMPLOYEE_ID, BONUS_YEAR, SALARY)
  SELECT EMPLOYEE_ID,
         EXTRACT(YEAR FROM NOW()),
         SALARY
    FROM EMPLOYEES
    WHERE SALARY < 10000
  ON CONFLICT (EMPLOYEE_ID, BONUS_YEAR)
  DO UPDATE SET
    BONUS = EMP_BONUS.SALARY * 0.5;

SELECT * FROM EMP_BONUS;
employee_id  bonus_year  salary   bonus
103          2017        9000.00  4500.000
104          2017        6000.00  3000.000
105          2017        4800.00  2400.000
106          2017        4800.00  2400.000
107          2017        4200.00  2100.000
109          2017        9000.00  4500.000
110          2017        8200.00  4100.000
111          2017        7700.00  3850.000
112          2017        7800.00  3900.000
113          2017        6900.00  3450.000
115          2017        3100.00  1550.000
116          2017        2900.00  1450.000
117          2017        2800.00  1400.000
118          2017        2600.00  1300.000

使用ON CONFLICT子句多次运行同一操作不会生成错误,因为现有记录将重定向到 update 子句。

有关详细信息,请参阅 PostgreSQL 文档中的 INSERT不支持的功能