Oracle:使用 SQL 或 PL/SQL 查找动态 SQL 中的错误位置

Posted

技术标签:

【中文标题】Oracle:使用 SQL 或 PL/SQL 查找动态 SQL 中的错误位置【英文标题】:Oracle: Find the position of an error in dynamic SQL using SQL or PL/SQL 【发布时间】:2013-04-24 04:17:25 【问题描述】:

如何在 PL/SQL 或 SQL 的动态 SQL 语句中找到错误的位置?

从 SQL*Plus 我看到错误的位置,例如,无效的 SQL DML 语句:

SYS@orcl> SELECT
       2    X
       3  FROM
       4    TABLEX
       5  /
  TABLEX
  *
ERROR at line 4:
ORA-00942: table or view does not exist

SQL*Plus 用行号显示错误,并在发现错误的地方打印并用星号标记该行。

转换为动态SQL,我可以得到错误代码(SQLCODE)和错误信息(SQLERRM):

SYS@orcl> SET SERVEROUTPUT ON
SYS@orcl> BEGIN
       2    EXECUTE IMMEDIATE 'SELECT X FROM TABLEX';
       3  EXCEPTION
       4    WHEN OTHERS THEN
       5      DBMS_OUTPUT.PUT_LINE('SQLCODE:' || SQLCODE);
       6      DBMS_OUTPUT.PUT_LINE('SQLERRM:' || SQLERRM);
       7  END;
       8  /
SQLCODE:-942
SQLERRM:ORA-00942: table or view does not exist

但是如何获取错误在动态 SQL 字符串中的位置呢?

我看到 Oracle 提供了一个 SQL 通信区 (SQLCA),其中包含有关错误的有趣信息。特别是:

SQLCODE 和 SQLERRM 字段(可能是使用相应 PL/SQL 函数检索的数据的来源), SQLERRD 字段,其中提供“解析错误偏移量”的 SQLERRD(5) 元素。

是否可以从 PL/SQL 或 SQL 访问 SQLERRD?如果是这样,怎么做?如果不是,还有什么其他技术可以从 PL/SQL 或 SQL 中给出错误的位置?

(此处http://docs.oracle.com/cd/B28359_01/appdev.111/b31231/chapter8.htm#BABIGBFF SQLCA 已记录并使用 Pro*C 访问。)

(这里的答案how to declare SQLCA.SQLERRD? 似乎表明 SQLERRD 没有在 PL/SQL 中定义,因此无法访问。)

(Why doesn't Oracle tell you WHICH table or view does not exist? 此处的讨论给出了一些建议,以使用跟踪文件显示错误的 SQL,并在某些开发工具中显示错误的位置。)

【问题讨论】:

【参考方案1】:

你有一个用于提取dbms_utility中的错误消息的包

begin 
    .. generate error
exception when others then 
    dbms_output.put_line(
        dbms_utility.format_call_stack()      || chr(10) || 
        dbms_utility.format_error_backtrace() || chr(10) || 
        dbms_utility.format_error_stack())
end;

【讨论】:

DBMS_UTILITY 本身不足以在动态 SQL 语句 inside 中查找行号。 @ThinkJet 的第二个块将起作用,尽管将 SQL 包装在动态 SQL 块中仍然存在困难。 (第一个块并不总是有效 - 它不会为解析错误获得正确的行号,例如如果表名错误。)【参考方案2】:

通过动态 PL/SQL 运行语句会将相关的行号存储在错误堆栈中。

比如这个语句第4行有错误:

declare
    v_count number;
    v_bad_sql varchar2(32767) := 
        'SELECT
            X
          FROM
            TABLEX';
begin
    execute immediate v_bad_sql into v_count;
exception when others then
    begin
        execute immediate
            'begin for i in ( '||v_bad_sql||') loop null; end loop; end;';
    exception when others then
        dbms_output.put_line(sqlerrm);
    end;
end;
/

ORA-06550: line 4, column 4:
PL/SQL: ORA-00942: table or view does not exist
ORA-00942: table or view does not exist
ORA-06550: line 1, column 18:
PL/SQL: SQL Statement ignored
ORA-00942: table or view does not exist

这种方法有一些缺点:

    它需要一些额外的、丑陋的代码来捕获异常并重试 SQL。 该示例仅适用于选择。您需要针对插入、更新、删除、合并、动态 PL/SQL 等进行调整。通常您应该知道它是哪种 SQL 语句。如果运气不好,则需要解析语句,这可能非常困难。 如果整个 PL/SQL 语句在一行上,则列号错误。

【讨论】:

我试图通过打印有问题的行和另一行用星号标记问题的开始来模仿 SQL*Plus 的行为。我可以通过解析错误堆栈来获取所需的行数和列数,但是有没有更简单的方法? 据我所知没有更简单的方法。

以上是关于Oracle:使用 SQL 或 PL/SQL 查找动态 SQL 中的错误位置的主要内容,如果未能解决你的问题,请参考以下文章

Oracle PL/SQL:如何在长包中查找未使用的变量?

如何在 pl/sql (oracle 9i) 中查找数据类型的大小?

如何从 Oracle 10G PL/SQL 函数和过程中查找所有表引用? [复制]

Oracle:使用 SQL 或 PL/SQL 提取文件扩展名的最快方法

ORACLE 中的 PL/SQL、IF-ELSE 或 SELECT DECODE 哪个更好

去掉 PL/SQL 函数末尾的逗号 [Oracle PL/SQL]