Oracle - 将在 PL\SQL 过程中产生错误的静态 SQL 脚本记录到表中

问题描述 投票:0回答:1

我有一些执行静态 DML 语句并使用一些 pl\sql 变量的 PL\SQL 过程 在 where 子句中。 例如:

 procedure execute_insert is 

         l_parameter varchar2(100);

    begin

         l_parameter := '5';
    
         Insert into Destination_Table(id)
         select id
           from Source_Table st
         where 1=1
           and st.id = l_parameter;
    
 exception
        when others then
             (error logging)

 end execute_insert;

我希望能够编写此 sql 语句,以防它在日志表中产生错误。 我发现了这个https://tonyhasler.wordpress.com/2010/01/17/identifying-the-sql_id-of-static-sql-in-plsql-blocks/?unapproved=8391&moderation-hash=1cfb98260e508b941a539d746d98144f#respond

实际使用这个包:

CREATE OR REPLACE PACKAGE SQL_UTIL as 
   FUNCTION GET_LAST_SQLTEXT RETURN CLOB;
end SQL_UTIL;

/


CREATE OR REPLACE PACKAGE BODY SQL_UTIL
AS
   FUNCTION GET_LAST_SQLTEXT
      RETURN CLOB
   IS
      l_sql_text   CLOB;
   BEGIN
      SELECT   sql_fulltext
        INTO   l_sql_text
        FROM      v$sql sq
               JOIN
                  v$session se
               ON     sq.sql_id = se.prev_sql_id
                  AND se.prev_child_number = sq.child_number
                  AND sid = SYS_CONTEXT('USERENV', 'SID');

      RETURN l_sql_text;
   END;
END SQL_UTIL;
/

并像这样使用它:

DECLARE
   x   NUMBER;
BEGIN
   select   1 INTO x FROM dual;
   DBMS_OUTPUT.put_line(sql_util.get_last_sqltext);
END;

但是这个解决方案不适用于我的情况,因为它不会打印 pl\sql 变量的值 - 例如在下面的 PL\SQL 块中:

   DECLARE
       x   VARCHAR2(10);
       y   VARCHAR2(50) := '5';
    BEGIN
       select  y INTO x FROM dual;
       DBMS_OUTPUT.put_line(sql_util.get_last_sqltext);
    END;

它将打印:

SELECT :B1 FROM DUAL

我想得到:

SELECT 5 FROM DUAL 

有没有其他方法可以在不使用动态sql的情况下实现这一点? 而且我也不想在声明中添加注释来追踪它。

提前谢谢您。

oracle plsql oracle19c plsql-package
1个回答
0
投票

您将无法获得

SELECT 5 FROM DUAL
,因为那不是 SQL 游标的文本。在 PL/SQL 中,
y
是局部变量。当在 SQL 语句中使用时,它充当绑定变量。实际的 SQL 游标是用占位符解析的:
SELECT :B1 FROM DUAL
。如果不是这种情况,则必须重新解析并为
y
的每个值创建一个新游标,这违背了绑定变量的全部意义。

仅仅为了日志记录机制的目的,绝对不值得使用动态 SQL 强制这些值的字面化。您将产生性能问题(每次执行时都会进行硬解析),并且还会乱扔共享池,最终导致数据库出现内存问题。大多数 DBA 都会强烈反对这种方法(这是正确的)。 大多数 PL/SQL 程序员依赖于异常处理程序内捕获的错误堆栈和调用堆栈。它们被发送到日志记录过程,该过程将它们(在匿名事务内)插入到日志记录表中,

没有

SQL 本身。但是行号可以轻松地用于在过程/包中定位 SQL,因此您实际上不会因不捕获 SQL 而丢失任何内容。 最后,使用

prev_sql_id

的方法不太可靠。在错误处理或日志记录机制中的某个位置发出额外的 SQL 语句将有问题的 SQL 推出

prev_sql_id
槽太容易了。如果您没有任何异常处理程序并且异常正在向客户端引发,则可以使用
after servererror on database
系统触发器,它提供了一些允许捕获违规 SQL 的伪变量,但此类触发器仅在异常发生时才会触发扔给客户端。在 PL/SQL 编程中,您通常会在代码中处理异常,以便触发器永远不会触发。
我建议使用普通的异常处理程序,并在其中使用从错误处理函数(如 

dbms_utility.format_error_stack

/

.format_error_backtrace
/
.format_call_stack
)获取的参数调用日志记录过程(您自己编写)。使用它在代码中查找 SQL,而无需尝试在运行时捕获和存储 SQL。
    

© www.soinside.com 2019 - 2024. All rights reserved.