系统的一个异常SQL的处理

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了系统的一个异常SQL的处理相关的知识,希望对你有一定的参考价值。

参考技术A 下面是在awr报告里面看到的有问题的sql,是9个变量的,在应用前台属于关联查询,在sqlplus里面手工执行检查实际执行情况如下:

下面是查询到的绑定变量值,可以通过查看v$sql_bind_capture视图来查看变量的实际值,如果时间比较久,可以使用如下的语句查看历史的绑定变量信息

以下是开启了autotrace 选项跟踪的手工执行情况,从执行效率上看是没有问题的。

从执行计划和表的数据量等方面判断如果sql的开销有问题,应该出现在表SAMS_CHECKINOUT上面,下面检查该表上面索引的创建语句看是否有问题

下面是在awr报告里面看到的有问题的sql,是9个变量的,在应用前台属于关联查询,在sqlplus里面手工执行检查实际执行情况如下:

下面是查询到的绑定变量值,可以通过查看v$sql_bind_capture视图来查看变量的实际值,如果时间比较久,可以使用如下的语句查看历史的绑定变量信息

以下是开启了autotrace 选项跟踪的手工执行情况,从执行效率上看是没有问题的。

从执行计划和表的数据量等方面判断如果sql的开销有问题,应该出现在表SAMS_CHECKINOUT上面,下面检查该表上面索引的创建语句看是否有问题

从上图可以看到,实际测试出来的执行计划跟awr报告上不同。

现在要对sql做测试

我们通过/*+ gather_plan_statistics */ 收集的相关执行计划及其统计信息与该SQL的AWR报告中的执行计划不同,且逻辑读的数量与AWR报告中的数值也相差巨大。因此,为了更准确的判断问题,按以下方法测试。
1、SQL在生产库(SAMS库的实例 1上,实例名为sams1 )上,在SQLPLUS中执行。
2、执行后,在同一SQLPLUS窗口中,立即执行以下命令:

结果如下:

1、在目录下创建一个脚本文件,用来获取更加相信的信息。
2、在SQLPLUS中,执行以下命令:@sql_rpt 3271368959 1 24114 24115 99vaabs5ptktb
4、执行完成后,在该目录下生成一个html文档,拿到更加详细的sql统计信息附带表的数据信息

初步分析如下:
1、该SQL执行一次的逻辑读为11130块次,其中第37步的逻辑读为6127块次,占了一半还多。而该步的操作是根据前面的获取到的ROWID,回表SAMS_CHECKINOUT获取"SC".“CHECKTIME"[TIMESTAMP,11], "SC"."VERIFYCODE"[CHARACTER,4], "SC"."SN"[NVARCHAR2,40], "SC"."INSYSTIME"[TIMESTAMP,11]四列的内容。
2、第38步对SAMS_ICLOCK表的全表扫描,对整个SQL的逻辑读也有较大贡献。但这个不是问题的关键

另外索引上有两个想法:
1、新建组合索引或改造已有索引,按如下顺序构建组合索引:
(BADGENUMBER, CHECKTIME, SN, VERIFYCODE, INSYSTIME)
2、在表SAMS_ICLOCK上创建组合索引,列名及顺序如下:
(SN, ALIAS)

这两个索引先暂时不创建,先从其他方面入手
由于在测试过程中,其生成的执行计划从未与AWR中显示的执行计划一致过。所以,这也许是造成不能模拟出2亿个块次逻辑读的一个原因。因此,把有问题的SQL的执行计划绑定到的测试SQL上。然后执行该测试SQL,并观察和分析测试SQL的执行过程和结果来做出进一步的处理。
为完成上述想法,需要用到ORACLE的SQL PROFILE在不改变SQL文本的前提下,改变其执行计划。操作方法如下:
1、在SQLPLUS中,生成问题SQL的创建SQL PROFILE的脚本。该脚本执行后,会要求分别输入SQL_ID和PLAN_HASH_VALUE的值。而我们问题SQL的SQL_ID是99vaabs5ptktb,PLAN_HASH_VALUE的值是4243346097。脚本执行完成后,会在运行SQLPLUS的当前目录中生成一个脚本文件。其名称在执行脚本过程中的结尾有显示。为描述方便,简称该生成的S脚本文件为“问题SQL脚本”。
2、再次执行该脚本,只不过这次输入测试SQL的SQL_ID和PLAN_HASH_VALUE。其SQL_ID为3kys9xsdjrm3b,PLAN_HASH_VALUE的值为561269195。为描述方便,简称该生成的脚本文件为“测试SQL脚本”
3、在文本编辑工具中分别打开上述两个脚本,将问题SQL脚本中出现在以下特征文字之间的文字(不包含特征文字 )复制并覆盖掉测试SQL脚本中同样位置的原文字:
h := SYS.SQLPROF_ATTR(
………
……….
……….
:signature := DBMS_SQLTUNE.SQLTEXT_TO_SIGNATURE(sql_txt);
4、将测试SQL脚本另存为一个文件(后缀名为.sql)
5、在SQLPLUS中执行第4步另存后的脚本。
6、在SQLPLUS中原封不动的执行原测试SQL。(注:执行前设置SQLPLUS格式,以避免格式混乱。比如 set lines 200 set pagesize 100 )
7、执行 select * from table(dbms_xplan.display_cursor('','','allstats projection last'));

如果正常生成脚本,没有报错信息出现在屏幕上,就是生成脚本成功。比如出现下面的提示就是正常的:

如何修改我的 PL/SQL 过程以进行异常处理?

【中文标题】如何修改我的 PL/SQL 过程以进行异常处理?【英文标题】:How can I modify my PL/SQL procedure to go to my exception handling? 【发布时间】:2015-05-31 02:27:55 【问题描述】:

我正在使用 SQL 开发人员编写一个过程。

目标是从一张表中获取排水系统的名称,并计算排水系统名称代码在另一张表中出现的次数。

我的程序有效,但是当我输入错误的值时,它不会进入异常部分。例如,当我输入“Mexico River”时,表中不存在此名称。所以,我希望我的异常部分意识到这是一个不正确的输入值。

我的问题是如何修改我的代码,以便它可以检测到不正确的值并转到我的异常部分。

下面是我的代码的 sn-p:

PROCEDURE number_of_rivers --second procedure with 1 parameter
  (input_sysName IN TBLDrainage.DRAINAGE_SYS%TYPE)
   is 

    -- Create a cursor 
    cursor c_river is 
      select code, drainage_sys 
      from TBLDRAINAGE 
      where DRAINAGE_SYS = input_sysName;
    v_rivercount Number;
    r_variable c_river %rowtype; 

  Begin

   FOR r_variable in c_river
   loop 
     select count (Drainage_sys) into v_rivercount
     from TBLRIVER
     where DRAINAGE_SYS = r_variable.code;
     DBMS_OUTPUT.PUT_LINE (UPPER(input_sysName) || ' has ' || v_rivercount || ' river(s) in the drainage system.');
  end loop;


EXCEPTION
  WHEN NO_DATA_FOUND THEN
    DBMS_OUTPUT.PUT_LINE ('Error: Please enter a valid drainage system name');
  WHEN OTHERS THEN
    DBMS_OUTPUT.PUT_LINE ('Error in finding system');
END ;

【问题讨论】:

【参考方案1】:

CURSOR..FOR 循环具有执行零次或多次的特性。它不会抛出 NO_DATA_FOUND。

有几个解决方案。一种是在循环中包含一个计数,然后引发异常。

l_count := 0;
FOR r_variable in c_river
loop 
  ....
  l_count := l_count + 1;
end loop;
if l_count = 0 then
  raise NO_DATA_FOUND;
end if;

另一个是在程序开始时验证输入参数。

begin
  open c_river;
  fetch c_river into r_variable;
  if c_river%notfound then  
    raise NO_DATA_FOUND;
  else
    select count (Drainage_sys) 
    into v_rivercount
    from TBLRIVER
    where DRAINAGE_SYS = r_variable.code;
    DBMS_OUTPUT.PUT_LINE (UPPER(input_sysName) || ' has ' || v_rivercount || ' river(s) in the drainage system.');
  end if;
  close c_river;
EXCEPTION
  WHEN NO_DATA_FOUND THEN
    DBMS_OUTPUT.PUT_LINE ('Error: Please enter a valid drainage system name');
    close c_river;
END ;      

在这个解决方案中,我删除了循环,因为我希望对排水系统的查找应该是唯一的并返回一条记录。如果您的数据模型不是这样,请重新设置循环。


我保留了游标及其行变量的名称,但您应该重新命名它们。它们用于选择排水系统而不是河流,它们的名称应该反映这一点。命名事物的纪律是一个很有用的习惯,因为误导性的变量名称会在更大的代码块中造成混淆。

此外,吞下这样的异常是非常糟糕的:

 WHEN OTHERS THEN
    DBMS_OUTPUT.PUT_LINE ('Error in finding system');

Oracle 有成千上万条错误消息:最好对错误消息不做任何处理,而不是将其丢弃。

【讨论】:

以上是关于系统的一个异常SQL的处理的主要内容,如果未能解决你的问题,请参考以下文章

如何修改我的 PL/SQL 过程以进行异常处理?

SQL记录-PLSQL异常

SPRING BATCH:嵌套异常是java.sql.SQLException:ORA-08177:无法序列化此事务的访问权限

springmvc在处理请求过程中出现异常信息交由异常处理器进行处理,自定义异常处理器可以实现一个系统的异常处理逻辑。为了区别不同的异常通常根据异常类型自定义异常类,这里我们创建一个自定义系统异常,如

Oracle存储过程的异常处理

cmds系统归并缓慢的处理过程 2017-2-16