【问题标题】:Trigger oracle: whats wrong?触发预言机:怎么了?
【发布时间】:2020-01-16 19:29:58
【问题描述】:

这是我的代码:

create trigger TriggerAdresse1
    before INSERT
    on Adresse
    for each row
declare
    enthaelt boolean;
begin
    if ((Provinz in (select Name from Provinz2)) and (Laendercode in (select Laendercode from Provinz2))) then
        enthaelt := true;
    end if;
    if(enthaelt:=false) then
        rollback;
    end if;
end;

如果属性 Provinz 或 Laendercode 不在表 Provinz2 中,我将尝试取消插入。 Datagrip 说它无效...

感谢您的帮助! 最好的问候

【问题讨论】:

  • 什么是Provinz
  • Provinz 是 Adresse 的一个属性
  • “Datagrip 说它无效...”不是 PL/SQL 编译错误消息。但是 PL/SQL 中没有 if x in (select ...) 语法。此外,if 条件周围没有括号(虽然它们不会导致编译错误,但它们只会激怒 PL/SQL 程序员)。
  • 另外,您不能在触发器中使用rollback。这将回滚整个事务,包括在触发语句之前所做的任何未提交的更改,这是不允许的,因为它可能会导致混乱。你所能做的就是引发一个异常,这将导致插入失败并出现错误堆栈。
  • 感谢您的 cmets,William!我们需要在 DataGrip 中创建一个检查:youtrack.jetbrains.com/issue/DBE-9944

标签: oracle plsql database-trigger datagrip


【解决方案1】:
  • Oracle 中的BOOLEAN 值可以有三个 值:TRUEFALSENULL,您没有初始化变量。
  • 您必须引用新值(默认为 :new
  • 默认情况下,触发器不能包含COMMITROLLBACK

您的代码必须是这样的:

create trigger TriggerAdresse1
    before INSERT
    on Adresse
    for each row
declare
    enthaelt INTEGER;
begin

    SELECT COUNT(*)
    INTO enthaelt 
    FROM Provinz2
    WHERE Name = :new.Provinz
       AND Laendercode = :new.Laendercode;
    if enthaelt = 0 then
        RAISE_APPLICATION_ERROR(-20001, 'Provinz oder Ländercode ungültig');
    end if;
end;

但是,您的要求应该使用 FOREIGN KEY Constraint 更好地实现

alter table Adresse add constraint Provinz_FK FOREIGN KEY (Provinz) 
  references Provinz2 (Name);

alter table Adresse add constraint Laendercode_FK FOREIGN KEY (Laendercode) 
  references Provinz2(Laendercode);

很可能Laendercode 不是唯一键,但Name+Laendercode 是。那么它会是这样的:

alter table Adresse add constraint Provinz_FK FOREIGN KEY (Provinz, Laendercode) 
  references Provinz2 (Name, Laendercode);

【讨论】:

  • PL/SQL 中没有if var in (select) 构造,即使你将它括在双括号中。
  • @WilliamRobertson 你是对的,我更正了我的代码。
  • 代码检查的好人选! youtrack.jetbrains.com/issue/DBE-9944
【解决方案2】:

if(enthaelt:=false) then 通过此语句,您的意图是执行比较,但是 := 执行分配。所以当你比较时,你应该使用如下:

if(enthaelt=false) then

【讨论】:

    【解决方案3】:

    我认为您打算检查插入的值,以便您真正打算:

    begin
        enthaelt := false;
        if ((:new.Provinz in (select Name from Provinz2)) and (:new.Laendercode in (select Laendercode from Provinz2))) then
            enthaelt := true;
        end if;
        if(enthaelt:=false) then
            rollback;
        end if;
    end;
    

    你还需要初始化变量。

    也就是说,实现此逻辑的正确方法是使用外键约束。很遗憾,您被教导使用触发器进行此检查。

    【讨论】:

      猜你喜欢
      • 2014-01-05
      • 2011-04-11
      • 2013-11-23
      • 1970-01-01
      • 2020-04-13
      • 1970-01-01
      • 2011-10-12
      • 2021-12-17
      • 2020-01-30
      相关资源
      最近更新 更多