pl/sql 触发器完整性约束问题

Posted

技术标签:

【中文标题】pl/sql 触发器完整性约束问题【英文标题】:pl/sql trigger integrity constraint issue 【发布时间】:2012-05-17 20:39:16 【问题描述】:

我的任务是创建触发器以在订单表存在时插入到日志表中

    插入 更新

还在周五下午 5 点到周一上午 9 点之间禁用插入/删除/更新。下面的解决方案涵盖了所有这些,但是因为它是一个前触发器,我不得不关闭完整性约束(这对日志表有影响吗?)

是否有人对我如何能够做到这一点并保持完整性约束(在 logono 列上)有任何建议?

我在考虑 11G 计算触发器,但在之前的答案中建议这不适合使用,加上向后兼容性的问题。

   CREATE OR REPLACE TRIGGER log_order
BEFORE INSERT OR UPDATE OR DELETE ON orders
FOR EACH ROW
DECLARE out_of_hours EXCEPTION;
BEGIN
IF INSERTING THEN
IF
 TO_NUMBER( TO_CHAR( SYSDATE, 'DHH24' ) ) BETWEEN 109 AND 517 THEN 


    insert into order_log values
 (order_log_PK.nextval, 
 (select user from dual), 
 :NEW.ono, 
 (select  SYSDATE from dual), 
 'Order Inserted' ) ;
ELSE
    RAISE out_of_hours;
END IF;
END IF;
IF UPDATING THEN 
IF
 TO_NUMBER( TO_CHAR( SYSDATE, 'DHH24' ) ) BETWEEN 109 AND 517 THEN 


    insert into order_log values
 (order_log_PK.nextval, 
 (select user from dual), 
 :NEW.ono, 
 (select  SYSDATE from dual), 
 'order updated' ) ;
ELSE
    RAISE out_of_hours;
END IF;
END IF;
IF DELETING THEN
IF 
 TO_NUMBER( TO_CHAR( SYSDATE, 'DHH24' ) ) BETWEEN 109 AND 517
 THEN 
RAISE out_of_hourS;
END IF;
END IF;
EXCEPTION 
    WHEN out_of_hours THEN
    dbms_output.put_line('there is not privelages at this time');
    RAISE_APPLICATION_ERROR(-20001, 'CANNOT UPDATE OUT OF HOURS');
END;

谢谢

编辑

完整性问题的出现是因为进入日志表的 :NEW.ono 值尚未插入到订单表中,因为这是一个前触发器,因此违反了 logono 外键。

【问题讨论】:

您需要禁用哪些完整性约束?我不确定我是否遵循了这样做的必要性。 在将 :NEW.ono 插入日志表时,因为它是一个前触发器,所以在 order 表中还没有引用它,所以日志表中的 FK 导致了问题 【参考方案1】:

您可以简化触发器,以便只检查周一上午 9 点到周五下午 5 点的条件一次,这样您就只有一个地方可以插入到日志表中,并且无需额外的异常和异常处理程序.

您可以将触发器设置为 AFTER INSERT 触发器,以便在 ORDERS 中已经存在数据之后执行触发器,以便您可以在 ORDER_LOG 中创建一个外键约束,该约束引用 ORDERS 中的 ONO 键.但是,想要创建这种约束似乎很奇怪。如果您将日志表创建为基表的子表,这意味着您将永远无法从ORDERS 中删除一行,因为总会有一个子行,或者您需要删除所有在删除之前,ORDER_LOG 中的行 ONO 。这些通常都不是特别可取的——一般来说,拥有一个日志表的意义在于它将记录所有操作,而不仅仅是对基表中仍然存在的行的操作。

CREATE OR REPLACE TRIGGER log_order
  AFTER INSERT OR UPDATE OR DELETE ON orders
  FOR EACH ROW
DECLARE 
  l_operation varchar2(30);
BEGIN
  if to_number( to_char( sysdate, 'ddhh24' ) ) between 109 and 517
  then
    if inserting
    then
      l_operation := 'Order inserted';
    elsif updating
    then
      l_operation := 'Order updated';
    end if;

    -- Note that I'm guessing at the names of the columns in order_log
    insert into order_log( order_log_pk, 
                           modify_user,
                           ono,
                           modify_date,
                           description )
      values( order_log_PK.nextval,
              user,
              :new.ono,
              sysdate,
              l_operation );
  else 
    raise_application_error( -20001, 'Cannot update out of hours' );
  end if;
END;

虽然在不列出要插入的列的情况下对表执行INSERT 可能在语法上是有效的,但这样做很危险。如果您将来添加另一列,即使不需要,您的 INSERT 也会开始失败。如果您明确列出列,触发器将继续工作。如果您没有明确列出列,则相对难以发现将数据插入错误列的错误。我猜到了列是什么——你必须替换正确的列名。

【讨论】:

很棒的东西。您是否看到在完全独立的触发器中完成日期检查的任何论据? @DylanJackson - 我不会将它放在单独的触发器中,但我会强烈考虑将它放在从触发器调用的单独过程中。我个人不喜欢像这样将日期检查折叠成一行代码,很容易错过一个极端情况。在这种情况下,我宁愿更冗长,因为更容易看到正在发生的事情并在精神上验证它是否按预期工作。例如,书面支票实际上允许在周五下午 5 点到 5:59:59 之间进行更新。 是的,我现在可以看到这一点,你指出来!是时候重新审视我之前的问题了。谢谢。

以上是关于pl/sql 触发器完整性约束问题的主要内容,如果未能解决你的问题,请参考以下文章

数据库触发器对于跨表完整性约束是不是安全?

是否可以同时拥有外键约束和保持参照完整性的触发器?

oracle 禁用唯一性约束该如何写语句? 我的表名是OEM_REPOSITORY

MySQL系列 MySQL的约束

数据库原理实验(openGauss)完整性控制

数据库触发器对于跨表完整性约束是否安全?