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

Sql 如何使用Oracle触发器(和域表)代替check约束来强制列范围,sql,oracle,plsql,triggers,oracle-sqldeveloper,Sql,Oracle,Plsql,Triggers,Oracle Sqldeveloper,此触发器(Oracle 12c)用于停止插入和更新列(price)大于变量的表(MainTable又名Room)中的行。变量的值取决于另一列(类型)。有三种“类型”(s、D、K),且“类型”的允许最高价格分别为100、150和200。触发器通过引用具有两列三行的域表(DomainTable aka RoomType)工作,如下[roomTypeCode(S、D、K)、maxPrice(100150200)],并确保: …如果new MainTable.type='S',则new MainTabl

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

…如果new MainTable.type='S',则new MainTable.price …如果new MainTable.type='D',则new MainTable.price …如果new MainTable.type='K',则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.

谢谢你的帮助

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

当您插入或更新时,由于仍然无效,您会收到ORA-04098错误。通过查询
user\u 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在一篇评论中指出的,第一个错误是因为在declare部分中有一个select语句;也许在这一点上,进行审查将是有益的

第二个错误是因为您的
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
,因为无论插入什么,都可能将其视为两个文字字符,而不是换行符


您可能还想考虑捕获<代码> NOYDATAYOND < /COD>并引发您自己的异常,因为这将指示新的房间类型不存在,因此不以任何价格有效。

您在<代码>声明节中选择< <代码> >。在使用触发器之前,编译它并检查它是否有效。编译后,使用
show errors
命令(或SQL Developer编译器日志)查看遇到的实际问题;或者查询
user\u errors
视图中的对象名称。非常感谢Alex!实际上,如果要查询表,最好让它也执行比较<代码>从RootType中选择count(*),其中RoomTypeCode=:newRec.Type和MaxPrice>=:newRec.price。这样,返回零表示价格超过最大值,或者类型代码未定义。“这个或那个是问题”错误消息是正确的。