Oracle Unique Constraint - 触发器以检查新关系中的属性值

hen*_*ald 1 sql database oracle triggers constraints

嗨,我无法正确获取我的sql语法.我想创建一个查看新添加的外键的唯一约束,查看新关联实体的某些属性以确定是否允许该关系.

CREATE or replace TRIGGER "New_Trigger"
AFTER INSERT OR UPDATE ON "Table_1" 
FOR EACH ROW
BEGIN
Select "Table_2"."number" 
(CASE "Table_2"."number" > 0
  THEN RAISE_APPLICATION_ERROR(-20000, 'this is not allowed');
END)
from "Table_1"
WHERE "Table_2"."ID" = :new.FK_Table_2_ID 
END;
Run Code Online (Sandbox Code Playgroud)

编辑:APC答案非常全面,但是让我觉得我以错误的方式做到了.

情况是我有一个具有不同权限级别的人员表,我想检查这些权限级别,例如,用户"Bob"具有低级别权限,并且他试图成为需要高权限的部门主管,因此系统防止这种情况发生.


有一个后续问题,它提出了一个相关的场景,但有不同的数据模型. 在这里找到它.

APC*_*APC 5

因此,您要强制执行的规则是,如果TABLE_2中的某些列为零或更少,则TABLE_1只能引用TABLE_2.嗯....让我们理清触发逻辑然后我们将讨论规则.

触发器应如下所示:

CREATE or replace TRIGGER "New_Trigger"
AFTER INSERT OR UPDATE ON "Table_1" 
FOR EACH ROW
declare
  n "Table_2"."number".type%;
BEGIN

    Select "Table_2"."number" 
    into n
    from "Table_2"
    WHERE "Table_2"."ID" = :new.FK_Table_2_ID; 

    if n > 0
    THEN RAISE_APPLICATION_ERROR(-20000, 'this is not allowed');
    end if;

END;
Run Code Online (Sandbox Code Playgroud)

请注意,您的错误消息应包含一些有用的信息,例如TABLE_1主键的值,用于在表上插入或更新多行时.


你在这里要做的是强制执行一种称为ASSERTION的约束.断言在ANSI标准中指定,但Oracle尚未实现它们.也没有任何其他RDBMS.

断言是有问题的,因为它们是对称的.也就是说,规则也需要在TABLE_2上强制执行.当您在TABLE_1中创建记录时检查规则.假设稍后某个用户更新了TABLE_2.NUMBER,因此它大于零:您的规则现在已经中断,但是在有人在TABLE_1上发出完全不相关的 UPDATE之后,您将不会知道它已被破坏,然后失败.呸.

那么该怎么办?

如果规则是实际的

如果TABLE_2.NUMBER为零,则TABLE_1只能引用TABLE_2

然后你可以在没有触发器的情况下执行它

  1. 在TABLE_2上为(ID,NUMBER)添加UNIQUE约束; 您需要一个额外的约束,因为ID仍然是TABLE_2的主键.
  2. 在TABLE_1上添加一个名为TABLE_2_NUMBER的虚拟列.将其默认为零并具有检查约束以确保它始终为零.(如果您使用11g,则应考虑使用虚拟列.)
  3. 更改TABLE_1上的外键,以便(FK_Table_2_ID,TABLE_2_NUMBER)引用唯一约束而不是TABLE_2的主键.
  4. 删除"New_Trigger"触发器; 你不再需要它,因为外键会阻止任何人将TABLE_2.NUMBER更新为零以外的值.

但是,如果规则真的是我在顶部制定它,即

如果TABLE_2.NUMBER不大于零,则TABLE_1只能引用TABLE_2(即负值可以)

那么你需要另一个触发器,这次是在TABLE_2上,在规则的另一面强制执行它.

CREATE or replace TRIGGER "Assertion_Trigger"
BEFORE UPDATE of "number" ON "Table_2" 
FOR EACH ROW
declare
  x pls_integer;
BEGIN

    if :new."number"  > 0
    then
        begin
            Select 1 
            into x
            from "Table_1"
            WHERE "Table_1"."FK_Table_2_ID" = :new.ID
            and rownum = 1;

           RAISE_APPLICATION_ERROR(-20001, :new.ID
                 ||' has dependent records in Table_1');
        exception
           when no_data_found then 
               null; -- this is what we want
        end;

END;
Run Code Online (Sandbox Code Playgroud)

如果TABLE_2中的记录引用了TABLE_2.NUMBER,则此触发器将不允许您将TABLE_2.NUMBER更新为大于零的值.它仅在UPDATE语句触及TABLE_2.NUMBER时触发,以最小化执行查找的性能影响.