【问题标题】:Convert MERGE statement to UPDATE statement将 MERGE 语句转换为 UPDATE 语句
【发布时间】:2019-10-11 12:37:01
【问题描述】:
MERGE INTO TABLE1 t1
USING TABLE2 t2
ON (t1.ID = t2.ID) 
WHEN MATCHED THEN UPDATE 
  SET t1.PHONE_NUMBER = CASE 
                          WHEN t1.type in ('A','B') THEN  t2.phone_number 
                          ELSE NVL(t2.phone_number, t1.phone_number)
                        END

需要将上面的MERGE 转换成UPDATE 语句。

我尝试了以下方法:

UPDATE TABLE1 t1 
   SET t1.PHONE_NUMBER = (
                          SELECT t2.PHONE_NUMBER 
                            FROM TABLE2 t2, TABLE1 t3 
                           WHERE t3.type in ('A','B') 
                             AND t3.ID = t2.ID
                         )

如何在上述实现中加入CASE

【问题讨论】:

  • 转换它没有意义;你的 where 子句意味着 ELSE 永远不会是真的
  • 为什么要切换到 UPDATE 语句?
  • 我想切换到 UPDATE 因为我的 MERGE 是 500 行加过程的一部分,并且由于某种原因,在执行过程时 MERGE 语句不会执行。当我明确运行 MERGE 时,它会给出所需的结果。所以想看看将其转换为 UPDATE 是否会与过程一起实际执行。

标签: sql oracle sql-update sql-merge


【解决方案1】:

MERGE 语句将更有效率,您可能应该使用它;但是,您可以使用:

UPDATE table1 t1
SET PHONE_NUMBER = NVL(
                     ( SELECT t2.phone_number
                       FROM   table2 t2
                       WHERE  t1.id = t2.id ),
                     CASE
                     WHEN t1.type IN ( 'A', 'B' )
                     THEN NULL
                     ELSE t1.phone_number
                     END
                   )
WHERE EXISTS ( SELECT 1 FROM table2 t2 WHERE t1.id = t2.id );

Oracle 设置

CREATE TABLE table1 ( id, phone_number, type ) AS
SELECT 1, 123456, 'A' FROM DUAL UNION ALL
SELECT 2, 123456, 'B' FROM DUAL UNION ALL
SELECT 3, 123456, 'C' FROM DUAL UNION ALL
SELECT 4, 123456, 'D' FROM DUAL UNION ALL
SELECT 5, 123456, 'E' FROM DUAL;

CREATE TABLE table2 ( id, phone_number ) AS
SELECT 1, 234567 FROM DUAL UNION ALL
SELECT 2, NULL   FROM DUAL UNION ALL
SELECT 3, 345678 FROM DUAL UNION ALL
SELECT 4, NULL   FROM DUAL;

输出

运行更新后:

SELECT * FROM table1

输出与MERGE 语句相同:

身份证 |电话号码 |类型 -: | ------------: | :--- 1 | 234567 |一种 2 | |乙 3 | 345678 | C 4 | 123456 | D 5 | 123456 |乙

db小提琴here


更新

如果您查看EXPLAIN PLAN 中的MERGE 语句:

|计划表输出 | | :------------------------------------------------ ------------------------------ | |计划哈希值:3423411568 | | | | -------------------------------------------------- ------------------------------ | | |身份证 |操作 |姓名 |行 |字节 |成本 (%CPU)|时间 | | | -------------------------------------------------- ------------------------------ | | | 0 |合并声明 | | 4 | 168 | 7 (15)| 00:00:01 | | | | 1 |合并 |表 1 | | | | | | | | 2 |查看 | | | | | | | | |* 3 |哈希连接 | | 4 | 268 | 7 (15)| 00:00:01 | | | | 4 |表访问已满|表 2 | 4 | 104 | 3 (0)| 00:00:01 | | | | 5 |表访问已满|表 1 | 5 | 205 | 3 (0)| 00:00:01 | | | -------------------------------------------------- ----------------------------->

那么它只读取TABLE1TABLE2 各一次。

将其与EXPLAIN PLANUPDATE 语句进行比较:

|计划表输出 | | :------------------------------------------------ ---------------------------------------- | |计划哈希值:735598124 | | | | -------------------------------------------------- ---------------------------------------- | | |身份证 |操作 |姓名 |行 |字节 |成本 (%CPU)|时间 | | | -------------------------------------------------- ---------------------------------------- | | | 0 |更新声明 | | 4 | 168 | 23 (22)| 00:00:01 | | | | 1 |更新 |表 1 | | | | | | | |* 2 |哈希连接半 | | 4 | 168 | 7 (15)| 00:00:01 | | | | 3 |表访问已满|表 1 | 5 | 145 | 3 (0)| 00:00:01 | | | | 4 |表访问已满|表 2 | 4 | 52 | 3 (0)| 00:00:01 | | | |* 5 |表访问完全 |表 2 | 1 | 26 | 3 (0)| 00:00:01 | | | -------------------------------------------------- ---------------------------------------- |

然后它将从TABLE1 读取一次,TABLE2 读取两次;所以MERGE 可能是一个性能更高的查询......但如果你愿意,你可以用UPDATE 来做到这一点。

db小提琴here

【讨论】:

    【解决方案2】:

    您可以使用带有RIGHT JOIN 的嵌套子查询:

    update table1 t 
       set t.phone_number = (
                              select tt.phone_number
                                from( 
                                     select case 
                                            when t1.type in ('A','B') then  t2.phone_number 
                                            else nvl(t2.phone_number, t1.phone_number)
                                            end as phone_number, 
                                            nvl(t2.ID,t1.ID) as ID
                                       from table2 t2
                                      right join table1 t1 
                                         on t1.ID = nvl(t2.ID,t1.ID)
                                     ) tt 
                                where tt.ID = t.ID
                               );
    

    Demo

    谢谢你的小提琴@MTO

    【讨论】:

    • 这会给出与MERGE 相同的结果行。唯一的细微差别是它将更新每一行,包括那些与MERGE 过滤的t1.id = t2.id 子句不匹配的行。对于小型数据集,这可能不是问题,但它可能会导致不必要的 I/O,当表的大小扩大时可能会产生性能问题。
    • 我认为您的意思是用t1.ID = nvl(t2.ID,t1.ID) 替换连接条件t1.ID = t2.ID ..,不是吗?
    • 不,我说的是 OP 的 MERGE 语句中的 WHEN MATCHES 条件;是的,您正在关联NVL(t2.id,t1.id) 上的子查询,但您没有过滤器来防止不必要地更新行。如果您查看 dbfiddle 则 MERGE 语句仅更新 4 行,但此查询更新所有 5 行。对于一张大桌子,这可能会导致很多不必要的工作。
    猜你喜欢
    • 1970-01-01
    • 2018-09-17
    • 1970-01-01
    • 1970-01-01
    • 2020-04-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多