merge语句导致的ORA错误分析(r9笔记第67天)

时间:2022-05-04
本文章向大家介绍merge语句导致的ORA错误分析(r9笔记第67天),主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

最近处理了好几起关于merge导致的问题,其实看到merge语句内心也还是蛮纠结的,这一次还是碰到了问题,简直无语了。

先交代下问题的背景。有一套OLTP环境和OLAP环境需要同步一部分数据,都是在每天的半夜开始,OLAP的库的一个表数据会根据增量的逻辑从OLTP库中同步,有两种方式,一种是OLAP从OLTP中去抓取,另外一种是OLTP推送给OLAP。看起来表达的意思是差不多的,实现起来就是完全不同的风格,即一种主动一种被动,而对于大部分的应用需求来看,还是更倾向于OLAP从OLTP中去抓取这种方式,要不OLTP端还是存在一定的耦合度。

但是目前的情况是OLTP主动推送给OLAP,当然这种设计方式看来达到的效果都是一致的,而且因为历史原因,也是一直这么用的。 在一次升级以后,借着升级的机会,而且很巧又碰到了老版本兼容merge语句的问题(又是merge),所以就自然而然想一并做个改进,把这个问题规范起来。 改进以后,OLAP会从OLTP中抓取数据,原来的逻辑完全不用修改,只是在两个表关联的地方,使用到了DB link来处理,所以代码层面可以认为是没有其它的变化,而且在部署到OLAP端后,我创建了一个小表还测试了一把,没有发现问题,所以简单修改配置之后,就部署完成了,还给开发的同事讲解了这个改进的意义,自己也还是蛮高兴的。 第二天同事就主动找我,说增量数据没有同步过来,带着程序员最常用的口吻“不可能啊”,“我这里运行都好好的”之类的想法,我查看了后台的JOB运行日志,结果发现日志中竟然抛出了一个ORA错误。 ORA-30926: unable to get a stable set of rows in the source tables ORA-06512: at line 32 这个错误看起来似懂非懂,比较模糊,初步感觉是表结构哪里出现了不一致的情况。 实现的大体思路如下: insert into test.tmp_usercenter select *from xxx@OLAP where xxxx; --通过这种方式获得增量数据

然后把增量数据插入test.test_user_center中。不匹配则插入 merge into test.test_user_center a using (select * from tlbb.tmp_usercenter) b on (a.uin=b.uin) when matched then update set LAST_LOGOUT=b.LAST_LOGOUT, LAST_IP=b.LAST_IP, FIRST_GAMESTYLE=b.FIRST_GAMESTYLE when not matched then insert (CN, UIN, BILL_TYPE, PRESENT_POINT,。。。 OUTLINE_SCORE,FIRST_GAMESTYLE) values ( b.CN, b.UIN, b.BILL_TYPE, b.PRESENT_POINT,。。。 b.FIRST_GAMESTYLE); commit; 了解了大体的思路之后,我对里面涉及的历史表字段都进行了认真的比对,没有发现任何问题,增量的数据都进行了比对,都是和原来的生成方式一样的。 但是为什么这种方式就会出问题呢。这一点上我不好解释,也不好给其他人解释,所以我需要认真分析,看看到底是哪里的问题。 同时我认真比对了ORA-30926错误的解释,发现merge在处理一些DML的时候,如果同一记录被影响变更多次,则会出现这类问题,那也就意味着表中的数据有冗余的成分。 但是同样的数据,同样的逻辑,几乎同样的语句,怎么原来可以,现在不可以呢。 最后反复测试,发现原来还是DB link在这一点上有很大的差别,如果在正常情况下存在冗余数据或者重复数据的情况下,使用merge来更新是会抛出ORA-30926错误的。而使用DB Link竟然会把这个错误化解,而一直以来使用的在OLTP端推送数据至OLAP,在这个场景下使用了这样的方式,看似也是碰到了一些问题,最后采用的那种方式,我不知道之前的人是否意识到那个问题,或者可能认为那种方式不支持这种需求,而我在尝试改进的时候,就踩到了这个坑。当然我还是希望能够在OLAP端完成这个需求,而原来可以,我们就可以借鉴成功的思想,所以改进起来就是耍个小聪明。DB link可以绕过这个ORA错误,那我们就使用DB link,但是访问的是当前库的表数据。即我们创建一个DB link指向自己,然后通过DB Link的方式访问。这样反复测试,发现确实是可以的。 所以语句的逻辑就改为下面的形式: insert into test.tmp_usercenter select *from xxx@OLAP where xxxx; --通过这种方式获得增量数据

然后把增量数据插入test.test_user_center中。不匹配则插入 merge into test.test_user_center@OLAP_TEST a --创建一个DB link OLTP_TEST指向当前的数据库 using (select * from tlbb.tmp_usercenter) b on (a.uin=b.uin) when matched then update set LAST_LOGOUT=b.LAST_LOGOUT, LAST_IP=b.LAST_IP, FIRST_GAMESTYLE=b.FIRST_GAMESTYLE when not matched then insert (CN, UIN, BILL_TYPE, PRESENT_POINT,。。。 OUTLINE_SCORE,FIRST_GAMESTYLE) values ( b.CN, b.UIN, b.BILL_TYPE, b.PRESENT_POINT,。。。 b.FIRST_GAMESTYLE); commit; 这种方式能够改进原来的ORA错误,权当做一种变相的改进吧。