sql - 无效的新或旧规范错误

标签 sql oracle triggers

我正在尝试创建触发器来检查电话号码是否采用 (###) ###-#### 格式,如果是这样,则不会发生任何情况,如果不是,则将被修复;但是,如果数字超过 10 位,则会将其转为 NULL。

不幸的是,我在此触发器中不断收到无效的新或旧规范错误,但我不知道为什么。

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;

最佳答案

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

但是,还有一些更根本的错误。

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

    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格式已经正确。

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

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>

然后您可以决定如何处理出现错误的手机。正如我上面所说,如果您肯定想要将格式不正确的手机设为 NULL,那么插入与此模式匹配的数据。如果有人触及代码,检查约束将确保数据仍然采用正确的格式。


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

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;

这会检查(使用 bool 逻辑)结果是否为 REGEXP_LIKE()函数为假。如果是,那么它会将电话设为 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>

... instead of setting a phone number to null :new.vendor_phone := null; how would you make so it can automatically modify the phone number into the correct format? (###) ###-####

这实际上是 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 位数字,将它们拆分为子表达式,然后将它们放入正确的格式。有很多方法可以做到这一点,这可能不是最快的,但它使您的意图最清晰。

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

关于sql - 无效的新或旧规范错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29226053/

相关文章:

SQL Server - 与 NULL 相比非常慢

c# - 使用 SQL (SDF) 数据库中的项填充 WPF 列表框

sql - 在 postgresql 中设置 extra_float_digits = 3

java - JPA查询Oracle DB中的json字段

sql - ORA-00904 : invalid identifier in subquery

sql - 缺少空关键字 Oracle 12C

azure - 在每月的第三个工作日触发 Azure 逻辑应用

javascript - GTM自定义JS触发器

amazon-web-services - AWS : Trigger event if DynamoDB table row is not updated in the last 30 minutes

php - 我该如何查询?