今天对之前描述的问题 一条insert语句导致的性能问题分析(一) 进行了进一步的补充。
有一条insert语句的主要性能瓶颈在于insert子句中的查询语句,查询中的主要资源消耗在于对两个表进行了多次关联
语句主要的结构如下:

insert into xxxxx   (select * from TEST_vip_new minus select * from TEST_vip_new_bak 
 
         ) a left join TEST_vip_new_bak b 
 
         on a.cn=b.cn


对于这个test_vip_new和test_vip_new_bak我产生了疑问,觉得这个临时表test_vip_new_bak有些多余。带着这种思路分析,看起来逻辑很简单啊,于是就联系了开发的同学,一起讨论一番。
但是讨论完之后,还是让我有些不知所措。
首先,语句为什么要多次关联,自己做了一个小的测试,感觉这种关联方式还是有些多余。

SQL> create table a (id number); 
 
 Table created. 
 
 SQL> create table b (id number); 
 
 Table created. 
 
 SQL> insert into a values(1); 
 
 1 row created. 
 
 SQL> insert into a values(2); 
 
 1 row created. 
 
 SQL> insert into b values(1); 
 
 1 row created. 
 
 SQL> select * from a minus select * from b; 
 
         ID 
 
 ---------- 
 
          2 
 
 SQL> select *from a left join b on a.id=b.id; 
 
         ID         ID 
 
 ---------- ---------- 
 
          1          1 
 
          2 
 
 如果按照这样的思路,仿照原来的结构输出就是下面这样的结果。 
 
 SQL> select *from (select * from a minus select * from b) a left join b on a.id=b.id; 
 
         ID         ID 
 
 ---------- ---------- 
 
          2 
 
 但是和开发讨论了一番,发现我的想法有些简单了,具体的场景中数据过滤的逻辑比上面这种略微复杂一些。 
 
 通过一个两个测试表来模拟。 
 
 create table a (id1 number,id2 number);create table game_new (id1 number,id2 number); 
 
 create table game_new_bak (id1 number,id2 number); 
 
 如果原来的数据内容为(1,1),在运行存储过程之后,会修改为(1,2) 
 
 SQL> insert into game_new values(1,2); 
 
 1 row created. 
 
 然后存储过程在运行过程中,会插入一些新的数据,假设为(2,2) 
 
 SQL> insert into game_new values(2,2); 
 
 1 row created. 
 
 而临时表game_new_bak中的数据是存储过程运行之前的数据状态,即(1,1) 
 
 SQL> insert into game_new_bak values(1,1); 
 
 1 row created. 
 
 按照这种情况,两个表做了minus操作之后会输出两行,即修改之后的数据和新增的数据。 
 
 而这个需求需要实现的是,根据id1进行匹配,把修改前的id2一并输出。这样就知道修改前是什么样的数据了,如果是新增的,那这列的值就保持为空。 
 
 SQL>   select a.id1,a.id2,b.id2 from (select * from game_new minus select *from game_new_bak)  a left join game_new_bak b on a.id1=b.id1      
 
        ID1        ID2        ID2 
 
 ---------- ---------- ---------- 
 
          1          2          1 
 
          2          2 
 
 如果是这样的情况,就完全可以使用一次表关联就可以改进。可以用下面的形式。 
 
 select a.id1,a.id2,b.id2 from game_new a ,game_new_bak b where a.id1=b.id1(+) 
 
 所以就建议语句从原来的形式 
 
 SELECT A.CN,A.GRADE,A.RANK,A.SCORE,DECODE(SIGN 
 
 (A.RANK-(NVL(B.RANK,-1))),1,2,-1,3,0,1), 
 
 SYSDATE,(NVL(B.RANK,-1)),B.SIGN,B.FLAG,B.TAG,B.SCORE FROM ( SELECT * 
 
 FROM GAME_VIP_NEW MINUS SELECT * FROM GAME_VIP_NEW_BAK ) A LEFT JOIN 
 
 GAME_VIP_NEW_BAK B ON A.CN=B.CN 
 
 修改为: 
 
 SELECT /*+parallel(4)*/ A.CN,A.GRADE,A.RANK,A.SCORE,DECODE(SIGN 
 
 (A.RANK-(NVL(B.RANK,-1))),1,2,-1,3,0,1), 
 
 SYSDATE,(NVL(B.RANK,-1)),B.SIGN,B.FLAG,B.TAG,B.SCORE FROM GAME_VIP_NEW a,cydba.GAME_VIP_NEW_BAK B 
 
 where  A.CN=B.CN(+)


考虑到执行的情况和资源情况,加了一个并行,可以在一定程度上缓解这个问题。
在本地的环境中进行了测试,发现几分钟就可以轻松搞定,做了基本的确认,就和开发进行了反馈,对线上的存储过程内容进行了修改。
这个问题的解决也就终于告一段落。