merge改写sql遇到的等效性问题

原语句如下(表名及字段名已改)

update mwm mwm
   set mwm.new_val = nvl((select sum(nvl(mws.pnl_qty, 0))
                                       from mws mws
                                      where mws.o_id =
                                            mwm.o_id
                                        and mws.w_id =
                                            mwm.w_id
                                        and mws.a_num <=
                                            mwm.b_num), 0)
 where 1 = 1;

第一次改写如下,结果显然不对,因为未分组汇总啊

merge into mwm mwm
using (select sum(pnl_qty) over(order by a_num) as pnl_qty,
              w_id,
              o_id,
              a_num
         from mws) mws
on (mws.o_id = mwm.o_id and mws.w_id = mwm.w_id and mws.a_num = mwm.b_num)
when matched then
  update set mwm.new_val = mws.pnl_qty;


发出错语后改写如下,数据仍然不对

merge into mwm mwm
using (select sum(pnl_qty) over(partition by w_id, o_id order by a_num) as pnl_qty,
              w_id,
              o_id,
              a_num
         from mws) mws
on (mws.o_id(+) = mwm.o_id and mws.w_id(+) = mwm.w_id and mws.a_num(+) = mwm.b_num)
when matched then
  update set mwm.new_val = nvl(mws.pnl_qty, 0);


不得已,让网友发数据来进行分析,发现掉坑里了,mws.a_num <= mwm.b_num这两个值之间不是一一对应的,存在如(a_num in(1,3,5),  b_num in (2,3,6))类似数据,只好改写如下

merge into mwm mwm
using (select mwm.rowid as rid, sum(mws.pnl_qty) as pnl_qty
         from mwm mwm
         left join mws mws
           on (mws.o_id = mwm.o_id and
              mws.w_id = mwm.w_id and
              mws.a_num <= mwm.b_num)
        group by mwm.rowid) mws
on (mws.rid = mwm.rowid)
when matched then
  update set mwm.new_val = nvl(mws.pnl_qty, 0);
改写后与update对比测试,这次数据终于对了,下次改写时一定要再小心些



如果看了上面描述还不清楚的话,那我们建个环境模拟说明下

首先建立环境

SQL> drop table emp1;
 
Table dropped
SQL> drop table emp2;
 
Table dropped
SQL> create table emp1 as select * from scott.emp;
 
Table created
SQL> create table emp2 as select * from scott.emp;
 
Table created
SQL> alter table emp1 add sal1 number(18,2);
 
Table altered
SQL> alter table emp1 add sal2 number(18,2);
 
Table altered
SQL> delete from emp2 where rownum <=2; 
2 rows deleted
第一个错误写法如下,有9条数据更新不对
SQL> update emp1
  2     set emp1.sal1 = nvl((select sum(emp2.sal)
  3                           from emp2
  4                          where emp2.deptno = emp1.deptno
  5                            and emp2.empno <= emp1.empno),
  6                         0);
 
12 rows updated
SQL> merge into emp1
  2  using (select sum(emp2.sal) over(order by emp2.empno) as sal, empno, deptno
  3           from emp2) emp2
  4  on (emp2.empno(+) = emp1.empno and emp2.deptno(+) = emp1.deptno)
  5  when matched then
  6    update set emp1.sal2 = nvl(emp2.sal, 0);
 
Done
SQL> select count(*) from emp1 where sal1 <> sal2;
 
  COUNT(*)
----------
         9

在数据对应的情况下写法应如下

SQL> merge into emp1
  2  using (select sum(emp2.sal) over(partition by emp2.deptno order by emp2.empno) as sal, empno, deptno
  3           from emp2) emp2
  4  on (emp2.empno(+) = emp1.empno and emp2.deptno(+) = emp1.deptno)
  5  when matched then
  6    update set emp1.sal2 = nvl(emp2.sal, 0);
 
Done
SQL> select count(*) from emp1 where sal1 <> sal2;
 
  COUNT(*)
----------
         0

下面模拟数据不对应的情况,注意出现错误数据时empno的对应关系

SQL> update emp2 set empno = empno - 1 where rownum <=5;
 
5 rows updated
SQL> update emp1
  2     set emp1.sal1 = nvl((select sum(emp2.sal)
  3                           from emp2
  4                          where emp2.deptno = emp1.deptno
  5                            and emp2.empno <= emp1.empno),
  6                         0);
 
12 rows updated
SQL> merge into emp1
  2  using (select sum(emp2.sal) over(partition by emp2.deptno order by emp2.empno) as sal, empno, deptno
  3           from emp2) emp2
  4  on (emp2.empno(+) = emp1.empno and emp2.deptno(+) = emp1.deptno)
  5  when matched then
  6    update set emp1.sal2 = nvl(emp2.sal, 0);
 
Done
SQL> select emp1.deptno as deptno1, emp1.empno as empno1, emp2.empno as empno2
  2    from emp1, emp2
  3   where emp1.sal1 <> emp1.sal2
  4     and emp2.deptno = emp1.deptno
  5     and emp2.empno <= emp1.empno
  6   order by 1, 2, 3;
 
DEPTNO1 EMPNO1 EMPNO2
------- ------ ------
     10   7782   7781
     20   7566   7565
     30   7521   7520

     30   7654   7520
     30   7654   7653

     30   7698   7520
     30   7698   7653
     30   7698   7697
8 rows selected

这种情况下正确写法应如下

SQL> merge into emp1
  2  using (select sum(emp2.sal) as sal, emp1.rowid as rid
  3           from emp1
  4           left join emp2
  5             on emp2.deptno = emp1.deptno
  6            and emp2.empno <= emp1.empno
  7          group by emp1.rowid) emp2
  8  on (emp2.rid = emp1.rowid)
  9  when matched then
 10    update set emp1.sal2 = nvl(emp2.sal, 0);
 
Done
SQL> select count(*) from emp1 where sal1 <> sal2;
 
  COUNT(*)
----------
         0


你可能感兴趣的:(merge改写sql遇到的等效性问题)