无效的NEW或OLD规范错误

时间:2015-03-24 06:08:23

标签: sql oracle triggers

我正在尝试创建一个触发器来检查一个电话号码是否在(###)### - ####格式中,如果这样,那么什么都不会发生,如果不是那么它将被修复;但是如果数字中的数字超过10位,那么它将变为NULL。

不幸的是,我在这个触发器中一直收到无效的NEW或OLD规范错误,我不知道为什么。

CREATE OR REPLACE TRIGGER phone_correction
BEFORE INSERT OR UPDATE OF vendor_phone 
ON vendors 
FOR EACH ROW 
WHEN (NEW.vendor_phone != REGEXP_LIKE(vendor_phone, '^\(\d{3}\) \d{3}-\d{4}$'))
BEGIN 
  IF :NEW.vendor_phone != REGEXP_LIKE(vendor_phone, '^\D*(?:\d\D*){10}$')
    THEN
    :NEW.vendor_phone := null;
    DBMS_OUTPUT.PUT_LINE( 'The phone number is bad so setting to null.');
    ELSE
    :NEW.vendor_phone := REGEXP_LIKE(vendor_phone, '^\(\d{3}\) \d{3}-\d{4}$');
  END IF;  
END;

3 个答案:

答案 0 :(得分:1)

每当使用列名时,都必须指定:NEW。试试这个:

CREATE OR REPLACE TRIGGER phone_correction
BEFORE INSERT OR UPDATE OF vendor_phone 
ON vendors 
FOR EACH ROW 
WHEN (NEW.vendor_phone != REGEXP_LIKE(NEW.vendor_phone, '^\(\d{3}\) \d{3}-\d{4}$'))
BEGIN 
  IF :NEW.vendor_phone != REGEXP_LIKE(:NEW.vendor_phone, '^\D*(?:\d\D*){10}$')
    THEN
    :NEW.vendor_phone := null;
    DBMS_OUTPUT.PUT_LINE( 'The phone number is bad so setting to null.');
    ELSE
    :NEW.vendor_phone := REGEXP_LIKE(:NEW.vendor_phone, '^\(\d{3}\) \d{3}-\d{4}$');
  END IF;  
END; 

答案 1 :(得分:1)

您必须在任何地方指定 NEW OLD 之前的关键字 colon (:) 是指列。

此规则的唯一例外是 WHEN 子句。

  

在WHEN子句中指定的NEW和OLD关键字不是   考虑绑定变量,因此不以冒号(:)开头。   但是,必须在所有引用中使用冒号前面的NEW和OLD   除WHEN条款外。

因此,在您的代码中,您必须将条件中的新值引用为 :NEW

  

REGEXP_LIKE(vendor_phone

应该是,

REGEXP_LIKE(:NEW.vendor_phone

答案 2 :(得分:1)

这里有几个错误;正如其他人所说,您需要明确使用:new.:old.来引用触发器中的列,因此REGEXP_LIKE(vendor_phone变为REGEXP_LIKE(:new.vendor_phone

然而,还有一些更基本的错误。

  1. LIKE operator一样,REGEXP_LIKE()返回一个布尔值。因此,您的陈述:

    IF :NEW.vendor_phone != REGEXP_LIKE(vendor_phone, '^\D*(?:\d\D*){10}$')
    

    实际上是IF <string> != <Boolean>,它永远不会工作。

  2. 在触发器中使用DBMS_OUTPUT对您没有任何帮助,除非您打算在那里查看您为保留的任何日志已插入的每个行,然后执行某些操作以纠正所有问题。

  3. 静默删除数据是不好的做法,如果您要改变某些内容,那么最好提出错误并让调用代码/用户决定做什么。

    如果您不想让调用代码/用户执行任何操作,并且如果该列不符合模式,则肯定要将该列置为NULL,则不要尝试在以下位置插入数据所有

  4. ELSE语句中的IF条件是不必要的,因为:new.vendor_phone格式正确。

  5. 就个人而言,我完全删除触发器并添加约束以检查列中的格式是否是您想要的格式:

    SQL> alter table vendors
      2    add constraint chk_vendors_phone
      3        check (regexp_like(vendor_phone, '^\(\d{3}\) \d{3}-\d{4}$'));
    

    然后,当试图插入数据时,如果格式正确则成功,如果格式不正确则不成功:

    SQL> insert into vendors (vendor_phone)
      2  values ('(123) 123-1234');
    
    1 row created.
    
    SQL> insert into vendors (vendor_phone)
      2  values ('(123) 123-124');
    insert into vendors (vendor_phone)
    *
    ERROR at line 1:
    ORA-02290: check constraint (CHK_VENDORS_PHONE) violated
    
    
    SQL>
    

    然后,您可以决定如何处理有错误的手机。正如我上面所述,如果您肯定想要将格式不正确的手机归零,则插入与此模式匹配的数据。如果有人触摸代码,检查约束将确保数据仍然是正确的格式。


    如果你绝对必须使用触发器,那么它可以简化为如下所示:

    create or replace trigger phone_correction
    before insert or update of vendor_phone
    on vendors
    for each row
    when (not regexp_like(new.vendor_phone, '^\(\d{3}\) \d{3}-\d{4}$'))
    begin
       :new.vendor_phone := null;
    end;
    

    这将检查(使用布尔逻辑)REGEXP_LIKE()函数的结果是否为false。如果是,则将手机置为NULL。以下是它工作的一个例子:

    SQL> create table vendors (id number, vendor_phone varchar2(100));
    
    Table created.
    
    SQL> create trigger phone_correction
      2  before insert or update of vendor_phone
      3  on vendors
      4  for each row
      5  when (not regexp_like(new.vendor_phone, '^\(\d{3}\) \d{3}-\d{4}$'))
      6  begin
      7     :new.vendor_phone := null;
      8  end;
      9  /
    
    Trigger created.
    
    SQL> insert into vendors
      2  values (1, '(123) 123-1234');
    
    1 row created.
    
    SQL> insert into vendors
      2  values (2, '(123) 123-124');
    
    1 row created.
    
    SQL> select * from vendors;
    
            ID VENDOR_PHONE
    ---------- --------------------
             1 (123) 123-1234
             2
    
    SQL>
    

      

    ...而不是将电话号码设置为null:new.vendor_phone:= null;您将如何制作,以便自动将电话号码修改为正确的格式? (###)### - ####

    这实际上是REGEXP_REPLACE()文档中的示例。为了使其更具可扩展性,我将从字符串中删除所有非数字字符,然后尝试转换。要删除非数字字符:

    regexp_replace(vendor_phone, '[^[:digit:]]')
    

    这意味着无需替换字符类[:digit:]中没有的所有内容。然后,要进行转换,您可以使用文档中描述的子表达式:

    regexp_replace(regexp_replace(vendor_phone, '[^[:digit:]]') 
                  , '^([[:digit:]]{3})([[:digit:]]{3})([[:digit:]]{4})$'
                  , '(\1) \2-\3')
    

    这将查找3({3})个数字两次,然后查找4个数字,将它们分成子表达式,然后将它们放入正确的格式。有很多方法可以做到这一点,这可能不是最快的,但它会使你的意图最清楚。

    我不会在触发器中执行此操作,而是在插入表格时执行此操作。更好,如果这是一个客户端应用程序,您应该确保您的数字格式正确,然后才能访问数据库。