一个SQL语句引发的ORA-00600错误排查(二)

继昨天一个SQL语句导致的ORA-00600错误之后,我给出了背景和初步的分析结果,今天来给出我的结论,当然说明原因不是我的本意,还有反思。
首先语句类似这样的形式:
MERGE INTO (SELECT * FROM TEST_SERVER_LOG WHERE BUY_TIME>=TO_DATE(:1 ,'YYYY-MM-DD HH24:MI:SS')
                               AND BUY_TIME<to_date(:2 ,'yyyy-mm-dd="" hh24:mi:ss')=""                               ="" and="" put_date="TO_DATE(:3" ,'yyyy-mm-dd'))="" t                    USING(SELECT
                           TO_CHAR(:4 ) AS SN,
                           TO_NUMBER(:5 ) AS GROUP_ID,
                          。。。
                           TO_NUMBER(:23 ) AS CLIENT_STYLE,
                           TO_CHAR(:24 ) AS GAME_TYPE
                         FROM DUAL) A
                   ON (T.SN=A.SN)
                   WHEN NOT MATCHED THEN
                  
INSERT(T.SN,T.GROUP_ID,T.SERVER_IP,。。。)
                  
VALUES(A.SN,A.GROUP_ID,A.SERVER_IP。。。)
运行后会报出ORA-00600,我在初步的分析之后排除了绑定变量的个数,表中数据量大的可能因素。
在经过一番周折之后,发现问题可能出在这个语句的结构上。
当然我换一个方式来说明,我可以随便创建一个表,然后模拟这个ORA-00600的错误。
创建测试表test_bug
SQL> create table test_bug as select * from dba_objects where rownum<1;
Table created.
然后使用如下的语句尝试生成执行计划。
SQL> explain plan for merge into (select * from test_bug where object_type='TABLE') t
       using (select :1 object_id,:2 object_name,:3 objet_type from dual) a
       on(t.object_id=a.object_id)
       where not matched then
       insert into test_bug(object_id,object_name,object_type) values(a.object_id,a.object_name,a.object_type);
explain plan for merge into (select * from test_bug where object_type='TABLE') t
*
ERROR at line 1:
ORA-00600: internal error code, arguments: [qcsfbdnp:1], [1], [], [4], [], [],
[], [], [], [], [], []
当然了我无意中埋下了几个探针,如果你看到语句哪里有问题,后续分析就会明白了。
这个语句里的问题我是可以保持了(select * from test_bug where object_type='TABLE') t 这个子查询,抛出了ORA-00600的错误,那么我再简化一番如何,简化为(select*from test_bug)t这个子查询,同样还是会抛出一样的错误。
explain plan for merge into (select * from test_bug ) t
       using (select :1 object_id,:2 object_name,:3 objet_type from dual) a
       on(t.object_id=a.object_id)
       where not matched then
       insert into test_bug(object_id,object_name,object_type) values(a.object_id,a.object_name,a.object_type);
explain plan for merge into (select * from test_bug ) t
*
ERROR at line 1:
ORA-00600: internal error code, arguments: [qcsfbdnp:1], [1], [], [4], [], [],
[], [], [], [], [], []
我们来回过头来翻翻旧账,刚刚的语句的问题在如下的几个地方,在解析的时候都没有抛出错误,可见问题是出在这些之前,那么推理只有test_bug相关的子查询了。


修复这几个语法之后,使用下面的方式就没有问题了。
explain plan for merge into test_bug t
       using (select :1 object_id,:2 object_name,:3 object_type from dual) a
       on(t.object_id=a.object_id)
       when not matched then
       insert (object_id,object_name,object_type) values(a.object_id,a.object_name,a.object_type);
明白了问题的原因,我们来反思一下,其实我们缩写的merge语句都是merge into table_xx using(xxxx) on (xxx)的形式
在table_xx的地方加入子查询,可能会让我们在联想到一些语句中使用子查询的DML方式,但是在merge语句中,这个地方就是大忌,所幸的是这个问题目前的测试没有发现对线上环境产生严重的影响,但是需要引以为戒。
而对于merge语句的更多反思,如下:
1.我所从事的一些调优工作中,对于merge的优化很难下手,因为这虽然是一个语句,但是有多重执行路径,执行计划没法确定,使用调优工具优化也给不出建议。
2.在10g的版本中,如果scheduler中使用PL/SQL块,包含有merge语句,使用dbms_metadata.get_ddl竟然无法查看到完整的DDL信息
3.如果你想查看到DDL的信息,如果通过解析dmp的方式,就会发现DMP对于这个语句的处理很是特殊,可能又会让你失望了。
这些问题会或多或少困扰到你,而我印象很深的案例,则是第1项中列举的,优化类的困扰。我举一个例子。
下面是我在一次系统监控中发现的一个性能问题,CPU使用异常,而经过分析发现瓶颈来源于数据库层面的一个SQL语句。

看到的语句类似这样的形式:

MERGE INTO UC_OPENPLATFORM_USER t
                  
USING (SELECT count(*) CNT from UC_OPENPLATFORM_USER where
USER_ID=:1 and PLATFORM=:2) tw
                  
ON (tw.CNT>0)
                  
WHEN MATCHED THEN UPDATE SET t.NAME=:3, t.UPDATE_DATE=SYSDATE
where USER_ID=:4 and PLATFORM=:5
                  
WHEN NOT MATCHED THEN INSERT(USER_ID, PLATFORM, NAME,
CREATE_DATE, UPDATE_DATE) VALUES(:6, :7, :8, SYSDATE, SYSDATE)
查看执行计划发现里面存在大量的全表扫描,资源消耗极高。
而这个语句的逻辑其实仔细看看还能明白,就是在插入一条记录前看看表中是否含有,如果没有就插入,否则更新,但是里面使用了count(*)的方式处理,过滤条件存在一些潜在的问题,而优化方式就是简化这种逻辑。改为如下的方式:

MERGE INTO UC_OPENPLATFORM_USER t

                  
USING (SELECT :1 USER_ID,:2
PLATFORM from DUAL) tw

                  
ON (tw.USER_ID=T.USER_ID
and tw.PLATFORM=t.PLATFORM)

                  
WHEN MATCHED THEN UPDATE SET t.NAME=:3, t.UPDATE_DATE=SYSDATE

where USER_ID=:4 and
PLATFORM=:5

                  
WHEN NOT MATCHED THEN INSERT(USER_ID, PLATFORM, NAME,

CREATE_DATE, UPDATE_DATE) VALUES(:6, :7, :8,
SYSDATE, SYSDATE)       

而对ORA-00600的这个问题,其实也可以进一步反思,这个merge使用只有一个场景,其实可以考虑使用INSERT语句来实现。
很多的事情都有两面性,merge语句就是如此,而且是一种特殊的存在,我依然记得很久之前的一次技术争论中,有人说道:判断一个技术的优劣,也需要看待,到底是它带来的问题更多还是解决的问题更多?   
确实如此。 

时间: 2024-08-02 11:17:13

一个SQL语句引发的ORA-00600错误排查(二)的相关文章

一个SQL语句引发的ORA-00600错误排查

作者介绍 杨建荣,[DBAplus社群]联合发起人.现就职于搜狐畅游,Oracle ACE-A.YEP成员,超7年数据库开发和运维经验,擅长电信数据业务.数据库迁移和性能调优.持Oracle 10G OCP,OCM,MySQL OCP认证,<Oracle DBA工作笔记>作者.   Merge是从Oracle 9i就引入的功能,它是有别于其他DML中的一种特殊语句,类似于MySQL中的 insert into on duplicate key操作,而且Merge功能更丰富,可以同时对一个表中的

一个SQL语句引发的ORA-00600错误排查(一)

最近有一个同事问我一个问题,说他运行一个SQL语句抛出了ORA-00600的错误,想让我帮忙分析一下,这种问题听了确实有兴趣,了解了问题的大体情况之后,发现这个问题还是值得分析分析的,因为只是客户端调用抛出异常,没有给服务器端带来什么致命的影响,这样的问题还是很耐人寻味的.     简单沟通后,我得到了同事提供的SQL语句和执行环境,语句类似下面的形式: MERGE INTO (SELECT * FROM TEST_SERVER_LOG WHERE BUY_TIME>=TO_DATE(:1 ,'

在oracle 数据库中查看一个sql语句的执行时间和SP2-0027错误_oracle

进入sqlplus SQL> set timing on SQL> SQL> select count(*) from comm_human_role; COUNT(*) ---------- 866 Elapsed: 00:00:00.05 以上数字输出分别是:Hours:Minutes:Seconds.Milliseconds 即用了0.05秒的时间执行,相当于50毫秒. 时间如果是这样的表示:00: 03: 235.78 总共用的时间为235.78秒, 不到4分钟,所以显示的是3分

怎么写出一个sql语句来表示销售的配件的数量

问题描述 怎么写出一个sql语句来表示销售的配件的数量 String sql = "select '配件A' label,12 value " + "union all " + "select '配件B' label,10 value "; 来修改这个sql来表示表中的内容 解决方案 啥玩意,你这不是已经有这些数据了吗,还要改什么

需要写一个SQL语句,大神帮我吧!在线等

问题描述 需要写一个SQL语句,大神帮我吧!在线等 我有一张产品供货价的表ProductPrice,该表有五个字段,分别是id,ProductID(外键关联产品ID),ChannelID(外键关联渠道ID),CustomerTypeID(外键关联客户类型ID),PriceSupply(供货价), 该表有三个外键,我最终的目的是得到该外键对应的供货价,其中一个ID变了供货价肯定就变了.看一下该表的的结构和数据. 但是我页面上只有两个下拉框控件,一个是客户,一个是产品.我只能拿到这两个对应的ID,渠

sql语句-表T(A)包含正整数,可能有重复,构造一个SQL语句查询不在表T中的最小正整数

问题描述 表T(A)包含正整数,可能有重复,构造一个SQL语句查询不在表T中的最小正整数 表T(A)包含正整数,可能有重复,构造一个SQL语句查询不在表T中的最小正整数,需要考虑表T包含或不包含正整数1两种情况.不能编程实现.考虑了半天没有很好的思路,请大家指点则个 解决方案 DECLARE @minvalue INT = select min(A) from T IF minvalue > 1 return 1 ELSE DECLARE @maxvalue INT = select max(A

sql语句-请教一个SQL语句如何写?

问题描述 请教一个SQL语句如何写? 求助大侠:表A中有字段a,b,c,d,e,f,但并不是所有字段都一定有值,其中有值字段原值为'201xxxx'现在要将有值字段由'201xxxx'替换为'2001xxxx',无值字段保留为空,请问SQL语句如何写?(其中'201xxxx'中的xxxx代表数字,比如20101或201010101,位数不定,也可能只为201)请赐教. 解决方案 update [A] set [a] = left(a2)+'0'+right(alen(a)-2) [b] = le

求教这两张表有什么联系写一个sql语句呀!

问题描述 求教这两张表有什么联系写一个sql语句呀! ![ 解决方案 一个sql语句 怎么写比较好在同一个sql语句中写不同条件的count数量,和查询半年的数据自学数据库之一个自己写的SQL语句 解决方案二: 库存管理里面的商品product_id 和商品管理里的no,是同一个值么,如果不是的话,那商品管理表里面还有一部分看不到的字段呢? 解决方案三: 解决方案四: 两张表的第一个ID字段是不是同一个?

mysql 如何通过一个sql语句搜出比例关系

问题描述 mysql 如何通过一个sql语句搜出比例关系 例如 一个表格里有 商品,价格,日期 三个字段 a 10.2,20151008 a 9.2,20151009 a 7.2,20151010 b 11.2,20151008 b 10.2,20151008 我要搜 在历史上各个商品价格低于10¥的次数占该商品出现的总次数的百分比,应该如何写mysql语句呢? 谢谢了 搜出结果要类似 a 2/3 b 0/2 这样的sql语句怎么写呢? 解决方案 假设表名是ProductPrice, 商品,价格