ORA-04098 尝试限制字符长度时触发错误

ORA-04098 trigger error when trying to restrict character length

我正在尝试为我的数据库创建一个触发器 table 以便用户只能输入 6-8 个字符长的邮政编码。但是,即使触发器没有显示任何错误,这似乎也不起作用。

代码如下:

create or replace trigger loc_postcode
before insert or update of postcode
on location
for each row
begin
    if ( LENGTH(:new.postcode) > 8) or ( LENGTH(:new.postcode) < 6)
         then    raise_application_error(0001,
             'The postcode must be between 6 and 8 characters long');
         end if;
end;​

和错误:

ORA-04098: trigger 'C3392387.LOC_ID' is invalid and failed re-validation

据推测,您想要长度而不是值:

create or replace trigger loc_id 
before insert or update of postcode
on location
for each row
begin
         if (length(:new.postcode) > 8) or (length(:new.postcode) < 6)
         then    raise_application_error(
             'The postcode must be between 6 and 8 characters long');
         end if;
end;

您的代码不会生成错误,因为 Oracle 允许您尝试比较字符串和数字。失败是当字符串不是数字格式时。

POSTCODE 似乎是一个字符串。由于您要检查其长度,因此需要使用 LENGTH 函数。

if ( LENGTH(:new.postcode) > 8) or ( LENGTH(:new.postcode) < 6)

或者我个人更喜欢:

if NOT LENGTH(:new.postcode) BETWEEN 6 AND 8 THEN

在您的版本中,您试图将 POSTCODE 的实际值与数字 6 和 8 进行比较,当字符串值无法转换为数字时会导致错误。

正如其他人所提到的,您的触发器的先前版本的问题在于,当您需要将字符串的长度与值进行比较时,您正在将字符串与数字进行比较。我不会对此进行任何进一步的详细说明。

当前错误的原因是您没有为用户定义的错误使用有效的错误代码。每 the documentation the RAISE_APPLICATION_ERROR procedure takes error codes in the range -20000 to -20999. Change the error code to -20001 and the trigger will work.

令您感到有些惊讶的是,您遇到了这样的错误。我原以为你会得到 "ORA-21000: error number argument to raise_application_error of 1 is out of range",正如可以证明的那样 in this SQL Fiddle。这可能是因为你在最后一个分号后有点狡猾。当我在文本编辑器中查看它时,它以十六进制显示 space,但是当我将它复制到 SQL Fiddle 时判断它的显示方式可能不是。它也可能是 Stack Exchange 渲染引擎的产物。

顺便说一句,0001 不是 有效的 Oracle 错误代码; 00001 是唯一约束违规,将被声明为 -00001(注意减号)。

但是,我不会这样做。触发器在使用时会产生额外的开销,并且会混淆可以在数据库中声明的约束。级联触发器也总是存在危险,这会使您的数据模型极其复杂。

更简单的方法是声明您的 POSTCODE 列最多为 8 characters/bytes(由您决定)并在该列上添加一个 check constraint确保邮政编码的长度为 6 个字符(或字节)或更长。这将您需要的逻辑嵌入到 table 的结构中(因此嵌入到 Oracle 的元数据中),使查看正在发生的事情变得容易得多。

如果您要将 table DDL 声明为如下所示(显然已大大简化):

create table location ( 
     id number
   , postcode varchar2(8)
   , constraint pk_location primary key (id)
   , constraint ck_location_postcode check (length(postcode) between 6 and 8)
     )

那么你可以得到同样的结果(working SQL Fiddle)。请注意,列 POSTCODE 的最大长度为 8,它处理上限并且有进一步的检查约束限制它。我已经定义了检查约束来处理上限和下限,以便您将来可以知道您打算将 8 作为上限。因此,更改列的大小不会破坏您的约束。这是一项安全功能,仅此而已,可以在不更改功能的情况下声明如下:

   , constraint ck_location_postcode check (length(postcode) >= 6)