SQLERRM 正在被覆盖

Posted

技术标签:

【中文标题】SQLERRM 正在被覆盖【英文标题】:SQLERRM is being overwritten 【发布时间】:2014-10-22 15:39:21 【问题描述】:

我正在使用 SQLERRM。但是有更多的程序并行运行,所以有时 SQLERRM 会被其他程序覆盖。并行运行的意思是我同时运行过程 a 和过程 b。SQLERRM 是一个全局函数,这是我的问题。如果在两个过程中都抛出异常,则可能会发生,过程 a 修改过程 b 中的错误消息,过程 b 收到错误消息。 这些程序使用简单的构造:

BEGIN
--do something/cutted off
EXCEPTION
        WHEN OTHERS THEN
            write_log(SYSDATE, 'ERROR_1', SQLERRM);

END;

这些过程调用 write_log ,声明为

 CREATE OR REPLACE PROCEDURE WRITE_LOG
 (
        TS_START IN DATE DEFAULT SYSDATE,
        ERR_CODE IN VARCHAR DEFAULT NULL,
        ERR_DESC IN VARCHAR DEFAULT NULL
 ) 
 IS

 BEGIN
  INSERT INTO LOG
        ( LOG_TS_START, LOG_ERR_CODE, LOG_ERR_DESC)
    VALUES
        (TS_START, ERR_CODE, ERR_DESC);


   COMMIT;

END WRITE_LOG;

如何避免覆盖 SQLERRM ?正在写入日志,但错误消息是错误的。

【问题讨论】:

你的意思是write_log处理的值不是你期望的吗? write_log 是如何定义的 - 它是直接引用 SQLERRM 本身还是过程参数? “并行运行”是指 write_log 是用 pragma autonomous_transaction 声明的吗? 欢迎来到PL/SQL 世界和常见错误。 EXCEPTION WHEN OTHERS?一个真正的错误。 @LalitKumarB - 我真的希望这是对发布的简化,但我想不会再阅读它了。我认为这与实际问题无关。真的不清楚那是什么...... 你可能是对的,但是错误堆栈永远不会与异常块相同。 为了完美调试,最好从各处移除接收块。 【参考方案1】:

首先,去掉PL/SQL代码中的常见bug。

WHEN OTHERS THEN 没有RAISE 也没有任何合理的逻辑只是无用的,无非是一个错误。

删除所有exception blocks,然后重试。并且,遵循良好的编码实践。

【讨论】:

+1 来自一个浪费了数周时间调试应用程序的人,这些应用程序静默失败,甚至不记录错误的行号。 100% 同意您的观点,但这并不能回答 OP 的问题。【参考方案2】:

过程WRITE_LOG 没有使用传入的参数来执行INSERT。试试看:

 CREATE OR REPLACE PROCEDURE WRITE_LOG
   (TS_START IN DATE DEFAULT SYSDATE,
    ERR_CODE IN VARCHAR DEFAULT NULL,
    ERR_DESC IN VARCHAR DEFAULT NULL)
 IS    
 BEGIN
   INSERT INTO LOG
     (LOG_TS_START, LOG_ERR_CODE, LOG_ERR_DESC)
   VALUES
     (TS_START, ERR_CODE, ERR_DESC);

   COMMIT;
END WRITE_LOG;

此外,在我看来,将错误记录到数据库表中是绝对错误的。因为您在日志记录过程中有一个COMMIT 语句,所以您将提交所有更改直到错误点,这很可能不是您想要做的。如果你愿意,你可以搞乱自主事务,但为了我的钱,最好的办法是登录到一个平面文件。

分享和享受。

【讨论】:

该函数使用参数,是一个错字(为了简化,我更改了一些名称)。编辑了查询。 当您发布代码时,请复制并粘贴真正使用的代码,而不是重新输入,这样可以消除此类问题。谢谢。【参考方案3】:

写入日志表很常见,但使用自治事务进行日志记录是标准做法。这是因为您不希望主事务中的回滚影响日志记录,或者日志记录中的提交影响主事务。 (@BobJarvis 示例不是自治的)。

一个简单的例子显示在here。

日志表还有其他一些需要考虑的事情。一方面,您应该有一个使用 sys_guid(在表上定义)或触发器上的序列的 log_id。此外,使用 write_log 可用于区分正在写入日志消息的过程(或会话)的上下文字段。

添加此设置后(包括上下文),然后再次(同时)运行过程 a 和过程 b,并在上下文中传递过程名称(a 或 b)。您将能够在表格中看到 a 和 b 日志消息。

【讨论】:

以上是关于SQLERRM 正在被覆盖的主要内容,如果未能解决你的问题,请参考以下文章

调用被覆盖的方法,超类调用被覆盖的方法

@ConditionalOnMissingBean 不能被覆盖

为啥 plist 中的项目会被覆盖?

linux 覆盖可执行文件的问题

NuxtJs Auth 具有多种策略,端点被覆盖

JHDF5 - 如何避免数据集被覆盖