RAISE_APPLICATION_ERROR 问题

Posted

技术标签:

【中文标题】RAISE_APPLICATION_ERROR 问题【英文标题】:RAISE_APPLICATION_ERROR issue 【发布时间】:2011-06-20 17:55:02 【问题描述】:

我开发了一个触发器来检查日期的有效性。它工作正常,因为它可以防止我存储无效日期,但我也收到一条奇怪的错误消息,我不知道为什么。我的代码如下:

CREATE OR REPLACE TRIGGER  "CHECKDATEVALIDITY" 
BEFORE INSERT OR UPDATE
ON Event
FOR EACH ROW
BEGIN
IF :NEW.day < 1 OR :NEW.month < 1 OR :NEW.month > 12
    THEN
            RAISE_APPLICATION_ERROR(-20101, 'Wrong date');
END IF;

IF :NEW.month = 4 OR :NEW.month = 6 OR :NEW.month = 9 OR :NEW.month = 11 
    THEN
        IF :NEW.day > 30
            THEN
                RAISE_APPLICATION_ERROR(-20101, 'Wrong date');
        END IF;
ELSIF :NEW.month = 2
    THEN
        IF (mod(:NEW.year, 4) = 0)
            THEN
                IF :NEW.day > 29
                    THEN
                        RAISE_APPLICATION_ERROR(-20101, 'Wrong date');
                END IF;
        ELSIF :NEW.day > 28
            THEN
                RAISE_APPLICATION_ERROR(-20101, 'Wrong date');
        END IF;
ELSE
    IF :NEW.day > 31
        THEN
            RAISE_APPLICATION_ERROR(-20101, 'Wrong date');
    END IF;

END IF;

END checkDateValidity;

我得到的错误是:

错误 ORA-20101:错误日期 ORA-06512:在“USER587.CHECKDATEVALIDITY”第 28 行 ORA-04088:执行触发器“USER578.CHECKDATEVALIDITY”时出错。

我还注意到,我从调用的 RAISE_APPLICATION_ERROR 旁边的行中得到了错误。什么发出错误?

【问题讨论】:

您认为错误信息有什么“奇怪”之处?这对我来说似乎完全合适。 嗨@p.cambell 谢谢你的评论。我觉得奇怪的是触发器按预期工作,我认为语法没有问题,我已经彻底检查过,我找不到任何问题。我不知道从哪里看这一点。 为日期验证等常见功能发明我们自己的逻辑的问题在于我们弄错了规则。能被 100 整除的年份不是闰年,除非它们也能被 400 整除。也就是说,29-FEB-1900 是平年,29-FEB-2000 是闰年。你的逻辑允许 29-FEB-1900,它不应该。 这就是RAISE_APPLICATION_ERROR 所做的——它会引发错误。这就是触发器阻止更新成功的方式——通过引发错误,导致 Oracle 停止处理语句。请注意,错误堆栈中的第一个代码是 ORA-20101——数值直接来自您对RAISE_APPLICATION_ERROR 的调用。代码完全按照您编写的方式执行。 您为什么不将日期存储为日期并让Oracle为您进行验证?如果您将日期存储在日期数据类型中,优化器还可以对日期执行智能操作。 【参考方案1】:

您认为“奇怪的错误信息”是什么?对我来说,它看起来像是一个完全合理的堆栈跟踪。在堆栈的底部,执行触发器时出错。下一行告诉您错误发生在第 28 行。堆栈的顶部是您的自定义错误消息和编号。这对我来说似乎很正常(尽管您似乎已经切断了一些与 ORA-06512 错误相关的错误文本)

ORA-20101: Wrong date
ORA-06512: on "USER587.CHECKDATEVALIDITY", line 28
ORA-04088: error while executing trigger 'USER578.CHECKDATEVALIDITY'.

如果您想匹配行号,请查看DBA_SOURCE。例如,这将向您显示触发器第 23-32 行的内容(违规行 +/- 5 行)。

SELECT line, text
  FROM dba_source
 WHERE owner = 'USER578'
   AND name  = 'CHECKDATEVALIDITY'
   AND line BETWEEN 23 and 32;

当然,我认为这是课堂练习,而不是您在现实世界中所做的事情。在现实世界中,您将存储在 DATE 列中,并让 Oracle 负责确保输入的日期有效。

【讨论】:

嗨@JustinCave 谢谢你的回答。我觉得奇怪的是触发器做了它应该做的事情,我真的不明白是什么导致了错误,我一遍又一遍地检查了语法,我找不到任何问题。我很无知。 @haunted85 - 触发器引发错误,可能是因为您尝试插入无效的日、月和年组合。触发器通过引发导致 DML 操作失败的错误来防止插入无效数据。这是预期的行为。这也是为什么在触发器中验证数据极为罕见的原因。【参考方案2】:

这意味着您(根据您自己的规则)插入了无效的日期。

如果日期大于 31,您的触发器将触发 ora-20101,它就是这样做的。

【讨论】:

以上是关于RAISE_APPLICATION_ERROR 问题的主要内容,如果未能解决你的问题,请参考以下文章

Oracle 中的 RAISE_APPLICATION_ERROR

触发 RAISE_APPLICATION_ERROR 但执行内部命令

为啥在 raise_application_error 未执行之前的触发器内部代码?

PL/SQL RAISE_APPLICATION_ERROR 参数问题

为啥不允许在 PL/SQL 触发器中使用 ROLLBACK 语句,但 RAISE_APPLICATION_ERROR 是?

ORA-4088、ORA-6512 使用触发器和 RAISE_APPLICATION_ERROR