【问题标题】:SQL Triggers containing sub-queries possible?可能包含子查询的 SQL 触发器?
【发布时间】:2014-09-07 04:56:04
【问题描述】:

希望能得到一些帮助;我正在使用 Oracle。

当前代码:

CREATE OR REPLACE TRIGGER TRG_SAMPLETRG
BEFORE INSERT ON CATEGORY_Details
FOR EACH ROW
DECLARE W_YEARSALIVE NUMBER(3);
BEGIN

  SELECT ((SYSDATE - animal_origindate)/365) INTO W_YEARSALIVE
  FROM animal
  WHERE animal_no = :NEW.animal_no;

  IF W_YEARSALIVE < 180 THEN
    RAISE_APPLICATION_ERROR(-20005, 'The animal has existed for only ' || W_YEARSALIVE || ' years');
  ELSIF W_YEARSALIVE IS NULL THEN
    RAISE_APPLICATION_ERROR(-20005, 'The animal has to have a origin date entered before evaluation.');
  ELSE
    DBMS_OUTPUT.PUT_LINE ('The animal is over 180 years and is not yet extinct and has been added');
  END IF;
END;

当我将动物添加到 category_details 表中时,如果该动物的起源日期(以年为单位)小于 180,或者如果它没有起源日期,则不会添加它并出现异常错误将被退回。

现在,我不需要对插入到 category_details 表中的每只动物评估触发器,我只想添加/评估仅来自以下 4 个不同类别的动物的触发器:Monera , 原核生物, 植物, 真核生物

任何其他不同的类别,即动物界、植物界,甚至都不应该被评估,也就是说,对与这些(或“其他”)类别相关的动物的任何插入没有限制。 p>

我尝试添加带有子查询的 WHEN 语句,如下所示: WHEN (:NEW.kingdom_no IN (SELECT Kingdom_no FROM Kingdom WHERE Kingdom_name IN ('Monera', 'Prokaryota', 'Plantae', 'Eukaryota'))

但您似乎无法将子查询添加到 TRIGGERS...

我找到了这个帖子:Oracle: Using subquery in a trigger

但是,看起来只有 1 个值/类别看起来很整洁,而我有 4 个,并且必须声明 4 个变量?

我还尝试在 IF 语句中放置子查询,但我认为这在 SQL/Oracle 中的语法无效。

感谢您的帮助。

编辑:表结构很简单: 3 个表:动物、category_details 和王国 category_details 的表有以下属性:animal_no, Kingdom_no 动物表具有以下属性:animal_no、animal_origindate Kingdoms 的表有以下属性: Kingdom_no, Kingdom_name

【问题讨论】:

    标签: sql oracle triggers subquery


    【解决方案1】:

    因为,根据 Oracle 文档[WHEN] 条件不能包含子查询,您必须将此部分放在触发器主体本身中,例如

    CREATE OR REPLACE TRIGGER trg_sampletrg
      BEFORE INSERT ON category_details
      FOR EACH ROW
    
    DECLARE
    
      w_kingdom_name kingdom.kingdom_name%TYPE;
      w_yearsalive   NUMBER(3);
    
    BEGIN
    
      SELECT kingdom_name
      INTO w_kingdom_name
      FROM kingdom
      WHERE kingdom_no = :new.kingdom_no;
    
      IF w_kingdom_name IN ('Monera', 'Prokaryota', 'Plantae', 'Eukaryota') THEN
    
        SELECT months_between(sysdate, animal_origindate)/12
        INTO w_yearsalive
        FROM animal
        WHERE animal_no = :new.animal_no;
    
        IF w_yearsalive < 180 THEN
          raise_application_error(-20005, 'The animal has existed for only ' || w_yearsalive || ' years');
        ELSIF w_yearsalive IS NULL THEN
          raise_application_error(-20005, 'The animal has to have a origin date entered before evaluation.');
        END IF;
    
      END IF;
    
    END;
    

    【讨论】:

    • 谢谢。在这篇文章之前我担心的一个问题是,“((SYSDATE - animal_origindate)/365)”和你所做的,months_between(sysdate,animal_origindate)/12之间有什么区别?他们不产生相同的输出吗?如果是这样,两者之间的显着差异是什么?您会选择其中一个而不是另一个?
    • 使用(sysdate-animal_origindate)/365 假设每年有365 天,但事实并非如此。 months_between 函数考虑了不同长度的年份,因此在 w_yearsalive 中给出了准确的结果
    • 感谢您的解释。
    【解决方案2】:

    您根本无法在 PL/SQL 中内联子查询。你可以做的是这样的:

    SELECT k.kingdom_name
    INTO   l_kingdom
    FROM   kingdom k
    WHERE  k.kingdom_no = :NEW.kingdom_no;
    
    IF l_kingdom in ('Monera','Prokaryota','Plantae','Eukaryota' THEN
       -- Do what needs to be done
    END IF;
    

    一般来说,您可以在一个行触发器中执行您想要的所有 DML(选择、更新、插入等)。经常惹恼人们的例外是您不能在附加了触发器的表上执行 DML - 在您的情况下为 CATEGORY_DETAILS。

    【讨论】:

    • 谢谢。但是有一个问题,为什么在附加了触发器的表上执行 DML 命令很烦人?是因为“你根本不能在 PL/SQL 中内联子查询”吗?很抱歉第一次没看懂。
    • 刚接触Oracle 触发器的人经常发现从同一个表中选择来检查新行与表中已有行的对应关系会很好。您实际上可以创建这样一个触发器,它将为单行 DML 操作编译和运行。但是如果你有一个更新或插入多行的语句,你会看到一个异常:ORA-04091: table XXXX is mutating, trigger/function may not see it。有很多方法可以解决它 - 但它们都不是很好。
    • 非常感谢。另外,请问您是否碰巧知道为什么我的 ELSE 语句没有正确执行?即插入一条未被异常捕获且位于 4 个王国/类别下的记录后,为什么没有出现输出行?我只目睹了“插入 1 行”的评论。我很确定它是之前输出的,我是否遗漏了一些明显的东西?
    • 我的猜测是它可以工作 - 但 dbms_output 仅显示您是否在 sql*plus 会话中完成了“设置服务器输出”。
    • 最后一件事,您是否知道是否可以从触发器中的另一个表中检索数据?也就是说,就我而言,raise_application_error 函数的输出可以是另一个表中的数据吗?即 Kingdom.kingdom_name 我收到的所有内容(在将其合并到错误函数之后)是:LS-00357:在此上下文中不允许表、视图或序列引用“kingdom.kingdom_name”
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-09
    • 2021-07-03
    • 2016-04-07
    • 1970-01-01
    • 2015-10-30
    • 1970-01-01
    相关资源
    最近更新 更多