【问题标题】:How to use Oracle trigger (and a domain table) in place of check constraint to enforce column range如何使用 Oracle 触发器(和域表)代替检查约束来强制列范围
【发布时间】:2015-04-25 13:09:18
【问题描述】:

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

...IF new MainTable.type = 'S', THEN new MainTable.price

...IF new MainTable.type = 'D', THEN new MainTable.price

...IF new MainTable.type = 'K', THEN new MainTable.price

这是我失败的尝试。


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.

感谢您的帮助!

【问题讨论】:

  • 您的DECLARE 部分中有SELECTs。
  • 在使用触发器之前,编译它并检查它是否有效。
  • 编译后使用show errors命令(或SQL Developer编译日志)查看实际遇到的问题;或在 user_errors 视图中查询您的对象名称。

标签: sql oracle plsql triggers oracle-sqldeveloper


【解决方案1】:

当您创建触发器时,您会看到诸如“编译时带有警告”或“错误:检查编译器日志”之类的消息。此时您可以通过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 并提出您自己的异常,因为这表明新房间类型不存在,因此在任何价格上都无效。

【讨论】:

  • 其实,如果你还是要出去查询表,不妨让它也进行比较。 select count(*) into roomCount from RootType where RoomTypeCode = :newRec.Type and MaxPrice >= :newRec.price;。这样一来,返回零意味着价格超过了 未定义类型代码的最大值。 “这个或那个是问题”错误消息很好。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-09-17
  • 2013-08-26
  • 1970-01-01
  • 1970-01-01
  • 2014-12-28
  • 2018-08-16
  • 2022-06-17
相关资源
最近更新 更多