ORACLE 11g 使用ROWNUM完美解决ORA-00600:内部错误代码

 

1,ORA-00600:内部错误代码

Oracle从11.2.0.1升级到11.2.0.4,开发人员报告说一个job运行失败,调试有报错信息,ORA-00600:内部错误代码,参数:[rwoirw: check ret val],[],[],[],[],ORA-06512:…,如下图所示:

 

C:\pic\oracle\2017040501.png

 

 

查看这个2000行的包体,查看到454行代码是一个create table as的很长的sql,如下所示:

 

/*===========================================================================*/
  /*构建ads_amp_pd_ma_merchant_sale表*/
/*===========================================================================*/
  PROCEDURE ads_amp_pd_ma_merchant_sale AS
    v_createsql varchar2(32767);
  BEGIN
    PKG_COMMON.drop_table('ads_amp_pd_ma_merchant_sale');
 
    v_createsql := '
    create table ads_amp_pd_ma_merchant_sale as
    (select
      --0 as ID,
      PKG_ADS_AMP_PD.PARTNER_ID as PARTNER_ID,
      0 as PROJECT_ID,
      0 as CONT_ID,
      zjwgsals.bis_shop_name as BRAND_NAME,
      zjwgsals.sales_money as SALE_AMOUNT,
      0 as BUDGET_AMOUNT,
      decode(zjwgsals.rent_square, 0, 0, round(zjwgsals.sales_money / zjwgsals.rent_square, 2)) as PERFORMANCE_VALUE,
      decode(zjwgsals.sales_money, 0, 0, round(zjwgsals.must_zj / zjwgsals.sales_money, 2)) as RENT_SALE_RATE,
      decode(zjwgsals.sort_name_root, ''超市'', ''100'', ''影院'', ''200'', ''百货'', ''300'', ''服装'', ''400'', ''儿童'', ''500'', ''餐饮'', ''600'', ''配套'', ''700'', ''800'') as COMMERCIAL_TYPE,
      zjwgsals.sort_name_root as COMMERCIAL_TYPE_NAME,
      zjwgsals.YEAR as YEAR,
      zjwgsals.MONTH as MONTH,
      zjwgsals.qz_year_month as DUTY_MONTH,
      ''0'' as IS_DEL,
      sysdate as CREATE_DATE,
      --null as UPDATE_DATE,
      PKG_ADS_AMP_PD.USER_ID as CREATER_ID,
      --null as UPDATER_ID,
      zjwgsals.bis_project_id as OUT_PROJECT_ID,
      zjwgsals.bis_cont_id as OUT_CONTRACT_ID,
      round(zjwgsals.sales_money * nvl(mustrent.royalty_ratio, 0) / 100 / 30.42, 2) as RENT_PER_DAY,
      round(zjwgsals.sales_money * nvl(mustrent.royalty_ratio, 0) / 100, 2) as RENT_PER_MONTH
    from
      ads_amp_pd_ma_contract cont
    inner join
      (select
        a.*,
        to_number(substr(qz_year_month, 1, 4)) as YEAR,
        to_number(substr(qz_year_month, 6, 2)) as MONTH
      from dws_pd_cont_zjwgsals_1m a
      where dt = PKG_COMMON.YESTERDAY_YYYYMMDD
        and a.status_cd not in (''3'', ''5'')
        and a.sals_data_flg = ''1''
      ) zjwgsals
    on cont.OUT_PROJECT_ID = zjwgsals.bis_project_id and cont.OUT_CONTRACT_ID = zjwgsals.bis_cont_id
    left outer join
      --提成百分比
      (select
        bis_cont_id,
        year,
        royalty_ratio,
        guaranteed_money
      from ods_pd_bis_must_rent
      where dt = PKG_COMMON.YESTERDAY_YYYYMMDD
      ) mustrent
    on zjwgsals.bis_cont_id = mustrent.bis_cont_id and (to_number(zjwgsals.YEAR) - to_number(to_char(cont.START_DATE, ''yyyy'')) + 1) = mustrent.year
    --TODO:
    where zjwgsals.bis_project_id in (''6D7E1C7AFAFB43E986670A81CF444235'', ''402834702db81ec3012dbca135f20c6a'')
    )';
    executeimmediate v_createsql;
    COMMIT;
  END;
 

 

 

 

 

2,官方的解决途径不是最佳方案

是动态sql执行的,创建一个表出错的问题,在11.2.0.1的version里面是ok的,到了11.2.0.4就出问题了,google了下,说是11.2.0.4的一个bug。

 

官方对该bug的描述如下,并提供了2种解决途径:

RefrenceBug 14275161 - ORA-600 [rwoirw:check ret val] on CTAS with predicate move around (Doc ID 14275161.8)

 

 

第一种是升级到12.1:

  --》这个刚升级完11.2.0.4再升级到12.1显然不太合适,而且万一到了12.1,又有新的bug出现,那就比较麻烦了。
 

第二种方法:通过设置隐含参数 _pred_move_around 为false来绕过该错误

          --》这个也尝试过了,不行,存储过程的动态sql里面,添加 altersystem set _pred_move_around =false;后报下面的错误出来:

SQL> call PKG_ADS_AMP_PD.build();

call PKG_ADS_AMP_PD.build()

ORA-04068: existing state of packages has been discarded

ORA-04061: existing state of package body "DW.PKG_ADS_AMP_PD" has been invalidated

ORA-04065: not executed, altered or dropped package body "DW.PKG_ADS_AMP_PD"

 

SQL>

 

 

所以说以上两种办法都不太可行,把create sql从动态存储过程里面拿出来,单独在11.2.0.4上面执行,是报错的,而且报错一模一样;突然想到难道是临时表的数量太多了导致的?然后马上尝试后面添加rownum < 100; 手动执行create sql语句结果成功了。

 

 

 

3,使用ROWNUM完美解决

思考了下,也许11.2.0.4里面对create tablexxx as select …. From …的限制比较严格(或者也许就是真的一个bug?),意味着在不知道后面的select … from …的总体数量的情况下或者数量已经超过了oracle的默认值比如1000这样,会提示ORA-00600的错误。按照这个思路我查询出来select … from ..的总数量,在后面加上and rownum<100000;,再次执行存储过程,哎,天降奇瑞,成功了。

 

然后再发散思维下,万一下回select的总数量超过100000了呢,那咋办,总不能每次都去select count(1) from …然后再create吧。按照测试的边界值理论,一个最大值ok了,那我尝试下最小值呢,用and rownum>-1;(因为有可能select 出来空记录)呢?尝试了下,存储过程执行成功了,and rownum>-1,那么最终改写的存储过程中的sql如下:

 

/*===========================================================================*/
  /*构建ads_amp_pd_ma_mercha,nt_sale表*/
/*===========================================================================*/
  PROCEDURE ads_amp_pd_ma_merchant_sale AS
    v_createsql varchar2(32767);
  BEGIN
    PKG_COMMON.drop_table('ads_amp_pd_ma_merchant_sale');
 
    v_createsql := '
    create table ads_amp_pd_ma_merchant_sale as
    (select
      --0 as ID,
      PKG_ADS_AMP_PD.PARTNER_ID as PARTNER_ID,
      0 as PROJECT_ID,
      0 as CONT_ID,
      zjwgsals.bis_shop_name as BRAND_NAME,
      zjwgsals.sales_money as SALE_AMOUNT,
      0 as BUDGET_AMOUNT,
      decode(zjwgsals.rent_square, 0, 0, round(zjwgsals.sales_money / zjwgsals.rent_square, 2)) as PERFORMANCE_VALUE,
      decode(zjwgsals.sales_money, 0, 0, round(zjwgsals.must_zj / zjwgsals.sales_money, 2)) as RENT_SALE_RATE,
      decode(zjwgsals.sort_name_root, ''超市'', ''100'', ''影院'', ''200'', ''百货'', ''300'', ''服装'', ''400'', ''儿童'', ''500'', ''餐饮'', ''600'', ''配套'', ''700'', ''800'') as COMMERCIAL_TYPE,
      zjwgsals.sort_name_root as COMMERCIAL_TYPE_NAME,
      zjwgsals.YEAR as YEAR,
      zjwgsals.MONTH as MONTH,
      zjwgsals.qz_year_month as DUTY_MONTH,
      ''0'' as IS_DEL,
      sysdate as CREATE_DATE,
      --null as UPDATE_DATE,
      PKG_ADS_AMP_PD.USER_ID as CREATER_ID,
      --null as UPDATER_ID,
      zjwgsals.bis_project_id as OUT_PROJECT_ID,
      zjwgsals.bis_cont_id as OUT_CONTRACT_ID,
      round(zjwgsals.sales_money * nvl(mustrent.royalty_ratio, 0) / 100 / 30.42, 2) as RENT_PER_DAY,
      round(zjwgsals.sales_money * nvl(mustrent.royalty_ratio, 0) / 100, 2) as RENT_PER_MONTH
    from
      ads_amp_pd_ma_contract cont
    inner join
      (select
        a.*,
        to_number(substr(qz_year_month, 1, 4)) as YEAR,
        to_number(substr(qz_year_month, 6, 2)) as MONTH
      from dws_pd_cont_zjwgsals_1m a
      where dt = PKG_COMMON.YESTERDAY_YYYYMMDD
        and a.status_cd not in (''3'', ''5'')
        and a.sals_data_flg = ''1''
      ) zjwgsals
    on cont.OUT_PROJECT_ID = zjwgsals.bis_project_id and cont.OUT_CONTRACT_ID = zjwgsals.bis_cont_id
    left outer join
      --提成百分比
      (select
        bis_cont_id,
        year,
        royalty_ratio,
        guaranteed_money
      from ods_pd_bis_must_rent
      where dt = PKG_COMMON.YESTERDAY_YYYYMMDD
      ) mustrent
    on zjwgsals.bis_cont_id = mustrent.bis_cont_id and (to_number(zjwgsals.YEAR) - to_number(to_char(cont.START_DATE, ''yyyy'')) + 1) = mustrent.year
    --TODO:
    where zjwgsals.bis_project_id in (''6D7E1C7AFAFB43E986670A81CF444235'', ''402834702db81ec3012dbca135f20c6a'')
    )' and rownum>-1;
    executeimmediate v_createsql;
    COMMIT;
  END;

 

 

 

 

 

 

 

 

 

OK,到此,问题完美解决,不改程序代码,不改系统参数,不用重启db,只需要在存储过程create语句where条件后面加上简单的and rownum>-1就搞定了。

 

 

参考文章:http://blog.csdn.net/wengtf/article/details/25713255

 

<think>好的,我现在需要帮用户解决Oracle数据库中的ORA-01422和ORA-06512错误。首先,我要理解这两个错误的含义。ORA-01422是因为在查询中期望返回一行结果,但实际返回了多行,而ORA-06512通常指出PL/SQL代码中发生异常的位置。 用户提到了具体涉及SCOTT.TAX_REVENUE表的问题,可能是在某个SELECT INTO语句中出现了问题。我需要分步骤思考解决方法: 1. **定位错误来源**:用户需要检查触发错误的SQL语句,特别是使用SELECT INTO的地方,因为这种语句要求严格返回单行。如果返回多行,就会抛出ORA-01422。 2. **检查查询条件**:确认WHERE子句是否足够精确,确保只返回一条记录。可能需要添加更多条件或使用唯一性约束的字段。 3. **异常处理**:建议在PL/SQL块中添加EXCEPTION处理,比如使用TOO_MANY_ROWS来捕获多行返回的情况,并记录错误信息,方便调试。 4. **使用聚合函数或ROWNUM**:如果预期可能有多行,可以修改查询,使用MAX或MIN等函数确保单行结果,或者用ROWNUM限制返回的行数。 5. **验证数据唯一性**:检查相关表中是否存在重复数据,特别是涉及主键或唯一索引的字段,如果有重复,需要清理数据或调整业务逻辑。 6. **动态SQL和游标处理**:如果无法避免多行返回,考虑使用显式游标遍历结果集,而不是SELECT INTO。 接下来,根据这些步骤,我需要整理出一个清晰的解决流程,确保用户能够逐步排查问题。同时,要注意引用提供的参考资料,比如引用[4]提到的重写查询或调整请求行数的建议,以及引用[3]中提到的行溢出可能的影响。 此外,用户可能需要预防措施,比如添加唯一性约束,避免未来出现类似问题。最后,生成相关问题,帮助用户深入理解错误原因和扩展解决方法。</think>以下分步骤说明如何排查和解决 Oracle 数据库中的 ORA-01422 和 ORA-06512 错误: ### 一、错误定位 1. **ORA-01422 根源**:当执行 `SELECT INTO` 语句时,数据库返回了多行数据但代码预期仅接收单行数据。常见于 PL/SQL 匿名块或存储过程中[^2][^4] 2. **ORA-06512 辅助定位**:该错误标记异常发生的堆栈跟踪位置,需结合具体代码行号排查[^3][^5] ### 二、具体解决步骤 1. **检查 TAX_REVENUE 表相关查询** ```sql -- 示例问题语句 BEGIN SELECT tax_rate INTO v_rate FROM scott.tax_revenue WHERE region_id = 100; -- 此条件可能返回多行 EXCEPTION WHEN TOO_MANY_ROWS THEN DBMS_OUTPUT.PUT_LINE('错误: 找到多个匹配记录'); END; ``` 2. **添加唯一性约束验证** ```sql -- 验证 region_id=100 的数据唯一性 SELECT region_id, COUNT(*) FROM scott.tax_revenue GROUP BY region_id HAVING COUNT(*) > 1; ``` 3. **修正查询方案** ```sql -- 方案1:添加保证唯一性的条件 SELECT tax_rate INTO v_rate FROM scott.tax_revenue WHERE region_id = 100 AND effective_date = (SELECT MAX(effective_date) FROM scott.tax_revenue WHERE region_id = 100); -- 方案2:使用聚合函数 SELECT MAX(tax_rate) INTO v_rate FROM scott.tax_revenue WHERE region_id = 100; ``` ### 三、预防措施 1. 在关键字段上创建唯一索引 ```sql CREATE UNIQUE INDEX idx_tax_region ON scott.tax_revenue(region_id, effective_date); ``` 2. 完善异常处理机制 ```sql BEGIN -- 业务逻辑代码 EXCEPTION WHEN TOO_MANY_ROWS THEN ROLLBACK; DBMS_OUTPUT.PUT_LINE('ERR-'||SQLCODE||': '||SQLERRM); WHEN OTHERS THEN ROLLBACK; RAISE; END; ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值