ITPub博客

首页 > 数据库 > 数据库开发技术 > 通用SQL开发的利器——MERGE(下)Merge妙用

通用SQL开发的利器——MERGE(下)Merge妙用

原创 数据库开发技术 作者:梁敬彬 时间:2025-07-09 14:42:27 0 删除 编辑


上期回顾

     通用SQL开发的利器——MERGE(上)Merge初探

     通用SQL开发的利器——MERGE(中)Merge初探 

 


 四.MERGE的巧妙运用

案例1:

需求为:将如下TEST记录的ID=1的NAME改为ID=2的NAME的值,把ID=2的NAME改为ID=1的NAME的值。

drop table test;
create table test (id number,name varchar2(20));
insert into test values (1,'a');
insert into test values (2,'b');
COMMIT;
SQL> SELECT * FROM test;
        ID NAME
---------- --------------------
         1 a
         2 b

如果执行如下:

UPDATE TEST SET NAME =(SELECT NAME FROM TEST WHERE ID=2) WHERE ID=1;

此时ID=1的NAME值已改变了,就不可能用如下来更新了

UPDATE TEST SET NAME =(SELECT NAME FROM TEST WHERE ID=1) WHERE ID=1;

如果是过程就很简单了,可以把原先的值先存储起来。但是是否单条SQL一定不行呢?

其实单条SQL是可以解决的,可考虑灵活利用MERGE特性!思路可考虑构造出一个虚拟表T,然后再根据此虚拟T表和真实的TEST表进行MERGE更新,就方便快捷的完成了。构造的虚拟表方法类似如下:

SQL> SELECT 1 id,(SELECT name FROM test WHERE id=2) name FROM DUAL
  2         UNION ALL
  3         SELECT 2,(SELECT name FROM test WHERE id=1) FROM DUAL
  4  ;
        ID NAME
---------- --------------------
         1 b
         2 a

   有了此思路,结合前面所学的MERGE知识,可以通过如下简洁优雅定的代码完成更新。

SQL> MERGE INTO TEST
  2  USING (SELECT 1 id,(SELECT name FROM test WHERE id=2) name FROM DUAL
  3         UNION ALL
  4         SELECT 2,(SELECT name FROM test WHERE id=1) FROM DUAL
  5         ) t
  6  ON (test.id = t.id)
  7  WHEN MATCHED THEN UPDATE set TEST.name = t.name
  8  ;
Done
SQL> SELECT * FROM test;
        ID NAME
---------- --------------------
         1 b
         2 a

注:如果是9I固定需要INSERT,所以需要随便加上如下内容

WHEN NOT MATCHED THEN 
INSERT   VALUES (1,'a')

本案例用的是MERGE的方法,当然,其中的构造虚拟表也是一个非常重要的思路,如果只是查询出改变后的结果而不是真实的进行更新,就可以不采用MERGE,直接可以采用如下方式取出结果

SQL> rollback;
Rollback complete
SQL> 
SQL> WITH T AS
  2  (SELECT 1 id,(SELECT name FROM test WHERE id=2) name FROM DUAL
  3         UNION ALL
  4         SELECT 2,(SELECT name FROM test WHERE id=1) FROM DUAL
  5         )
  6  SELECT test.id,t.name FROM test ,t
  7  WHERE test.id=t.id;
        ID NAME
---------- --------------------
         1 b
         2 a

案例2

通过MERGE可以得到一个非常有用的思想,就是如下:只要能查出更新后的结果集,就可利用该结果集来更新原表记录,即MERGE+ROWID方式。感谢NEWKID给予的指点,他精于使用此类方法,下文案例3中的复杂MERGE更新例子即来自NEWKID的精彩脚本。

本案例2来源于案例1的延伸,改变了案例1的处理思路,不再采用构造虚拟表T来关联TEST表的方式,而是直接把真实结果用SELECT的方式取出,然后利用这个结果集更新回原表中。

SQL> merge into  test using
  2  (
  3  WITH T AS
  4   (SELECT 1 id,(SELECT name FROM test WHERE id=2) name FROM DUAL
  5          UNION ALL
  6          SELECT 2,(SELECT name FROM test WHERE id=1) FROM DUAL
  7          )
  8   SELECT test.id,test.rowid as rn ,t.name FROM test ,t
  9   WHERE test.id=t.id
10  )      n
 11 on(test.rowid=n.rn)
 12  when matched then  update
 13  set test.name=n.name;
SQL> SELECT * FROM test;
        ID NAME
---------- --------------------
         1 a
         2 b

注:直接UPDATE一个子查询的写法也可行,但是却有很多限制,稍微复杂的查询都易出错。此时用MERGE是最好的办法,结合ROWID的方式,可快速准确的利用一个已查询出的结果集来更新自己,是一个非常好的思路的扩展,希望对大家有借鉴。

案例3

在文章的最后,举一个USING里面有复杂的连接、聚合、分析函数的综合性例子来加深读者的印象,从而更深入的理解MERGE的强大功能!脚本选自NEWKID在答网友提问的一次精彩回复,提问需求如下:

declare
cursor c1 is
SELECT art_no,stock 
FROM  tb_fin_art_stock
WHERE  run_date=to_date('&日期','yyyymmdd')-1  and art_no in (158756);
t_art tb_fin_art_stock.art_no%type;
t_stock tb_fin_art_STOCK.stock%type;
begin
open c1;
loop
        fetch c1 into t_art,t_stock;
        exit when c1%notfound;
 update tb_fin_art_stock set stock=t_stock+gor_qty+return_qty-sale_qty+stock_corr+DEL_CORR
                 WHERE run_date=to_date('&日期','yyyymmdd')
                        and art_no=t_art;
        commit;
end loop;
close c1;
end;

 以上代码,我想更新tb_fin_art_stock 这个表中某个货号某一天往后所有记录的 stock(库存)字段的值,每天的stock是根据前一天的stock字段的值加进货减销售得出来的。现在只能一天一天更新。我想问的是如何能输入日期范围, 比如10号到20号的记录,根据9号更改10号,根据10号再改11号,。。。。以此类推,一次就更新了。

MERGE精彩解决方案如下,有兴趣的读者自行研究,相信必有收获!

MERGE INTO tb_fin_art_stock t
USING (SELECT t.ROWID rid
             ,t2.stock+SUM(gor_qty+return_qty-sale_qty+stock_corr+DEL_CORR) OVER(PARTITION BY art_no ORDER BY run_date) AS stock
         FROM tb_fin_art_stock t
             ,(SELECT art_no,stock FROM tb_fin_art_stock WHERE run_date = lv_start_date-1) t2
        WHERE t.run_date BETWEEN lv_start_date AND lv_end_date
              AND t.art_no = t2.art_no
       ) n
USING (t.ROWID = n.rid)
WHEN MATCHED THEN UPDATE SET t.stock = n.stock;

在这里插入图片描述


全文完



公众号:收获不止数据库

微信:ljbyxl1213

系列回顾


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/9091385/viewspace-3090571/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论
历任中电福富研究院副理事长、中盾安信研究院副院长、宁德时代技术顾问等职务,受聘电信集团专家和国家十四五课题负责人,著有《收获,不止Oracle》《收获,不止SQL优化》等多本畅销数据库技术书籍。

注册时间:2024-02-06

  • 博文量
    162
  • 访问量
    198067