如何使用Oracle触发器(和域表)代替检查约束来强制列范围

时间:2015-02-24 01:41:11

标签: sql oracle plsql triggers oracle-sqldeveloper

此触发器(Oracle 12c)旨在阻止列(价格)大于变量的表(MainTable aka Room)中行的插入和更新。变量的值取决于另一列(类型)。有三种类型(S,D,K),'类型的允许最大价格分别为100,150和200。触发器通过引用具有两列和三行的域表(DomainTable aka RoomType)来工作,如下所示[roomTypeCode(S,D,K),maxPrice(100,150,200)]并确保:

...如果新的MainTable.type =' S',那么新的MainTable.price< DomainTable.maxPrice(S);

...如果新的MainTable.type =' D',那么新的MainTable.price< DomainTable.maxPrice(d);

...如果新的MainTable.type =' K',那么新的MainTable.price< DomainTable.maxPrice(K);

这是我的尝试不起作用。


CREATE TRIGGER Room_Type_Price_Range
  BEFORE INSERT OR UPDATE ON room
  REFERENCING NEW AS newRec
  FOR EACH ROW
  DECLARE
    SELECT maxPrice INTO singleRmMax FROM RoomType WHERE RoomTypeCode = 'S';
    SELECT maxPrice INTO doubleRmMax FROM RoomType WHERE RoomTypeCode = 'D';
    SELECT maxPrice INTO kingRmMax FROM RoomType WHERE RoomTypeCode = 'K';
  BEGIN
     IF (   (:newRec.type = 'S' AND :newRec.price > singleRmMax)
        OR  (:newRec.type = 'D' AND :newRec.price > doubleRmMax)
        OR  (:newRec.type = 'K' AND :newRec.price > kingRmMax)
        )
        RAISE_APPLICATION_ERROR(-20001, 'Price constraint violated.  
          \nCannot Insert/Update in this table.');
  END;

我的错误讯息:


04098. 00000 -  "trigger '%s.%s' is invalid and failed re-validation"
*Cause:    A trigger was attempted to be retrieved for execution and was
           found to be invalid.  This also means that compilation/authorization
           failed for the trigger.
*Action:   Options are to resolve the compilation/authorization errors,
           disable the trigger, or drop the trigger.

感谢您的帮助!

1 个答案:

答案 0 :(得分:2)

当您创建触发器时,您会看到一条消息,例如'编译时带有警告'或者'错误:检查编译器日志'。此时,您可以show errors查看编译失败的原因,或查看SQL Developer的编译器日志窗口。

当您插入或更新the invalid trigger is automatically recompiled时,但由于它仍然无效,您会收到ORA-04098错误。您仍然可以通过查询user_errors视图来查看错误:

select line, position, text
from user_errors
where type = 'TRIGGER'
and name = 'ROOM_TYPE_PRICE_RANGE'
order by sequence;

你的代码中有三个错误;仅显示每个的第一行:

LINE POSITION TEXT
---- -------- -------------------------------------------------------------------------------------------------
   2        5 PLS-00103: Encountered the symbol "SELECT" when expecting one of the following:
  10        9 PLS-00103: Encountered the symbol "RAISE_APPLICATION_ERROR" when expecting one of the following:
  11       50 PLS-00103: Encountered the symbol ";" when expecting one of the following:

正如David Faber在评论中指出的那样,第一个错误是因为你的声明部分中有一个select语句;或许在这一点上审查the structure of a subprogram会很有用。

第二个错误是因为您的IF没有THEN个关键字,第三个错误是因为您没有END IF。只需清理你要声明的内容并正确填充变量,你就会得到类似的东西:

DECLARE
  singleRmMax RoomType.MaxPrice%TYPE;
  doubleRmMax RoomType.MaxPrice%TYPE;
  kingRmMax RoomType.MaxPrice%TYPE;
BEGIN
  SELECT maxPrice INTO singleRmMax FROM RoomType WHERE RoomTypeCode = 'S';
  SELECT maxPrice INTO doubleRmMax FROM RoomType WHERE RoomTypeCode = 'D';
  SELECT maxPrice INTO kingRmMax FROM RoomType WHERE RoomTypeCode = 'K';

  IF (   (:newRec.type = 'S' AND :newRec.price > singleRmMax)
    OR  (:newRec.type = 'D' AND :newRec.price > doubleRmMax)
    OR  (:newRec.type = 'K' AND :newRec.price > kingRmMax)
    ) THEN
    RAISE_APPLICATION_ERROR(-20001, 'Price constraint violated.  
      \nCannot Insert/Update in this table.');
  END IF;
END;

你真的不需要三个变量,你可以只查询你感兴趣的房间类型:

DECLARE
  roomTypeMax RoomType.MaxPrice%TYPE;
BEGIN
  SELECT maxPrice INTO roomTypeMax
  FROM RoomType
  WHERE RoomTypeCode = :newRec.type;

  IF :newRec.price > roomTypeMax THEN
    RAISE_APPLICATION_ERROR(-20001,
      'Price constraint violated. Cannot Insert/Update in this table.');
  END IF;
END;

我还从错误消息中取出\n,因为插件可能会将其视为两个字面字符而不是换行符。

您可能还想考虑抓住no_data_found并提出自己的例外情况,因为这表明新房型并不存在,因此无论如何都无效。