【问题标题】:Oracle trigger: SELECT INTO, no data foundOracle 触发器:SELECT INTO,未找到数据
【发布时间】:2020-08-08 13:14:14
【问题描述】:

我有下表,用百分比来描述每个行星由哪些化学元素组成。

CREATE TABLE elem_in_planet
(
    id_planet INTEGER,
    element_symbol CHAR(2),
    percent_representation NUMBER CONSTRAINT NN_elem_in_planet NOT NULL,

    CONSTRAINT PK_elem_in_planet PRIMARY KEY (id_planet, element_symbol),
    CONSTRAINT FK_planet_has_elem FOREIGN KEY (id_planet) REFERENCES planet (id_planet),
    CONSTRAINT FK_elem_in_planet FOREIGN KEY (element_symbol) REFERENCES chemical_element (element_symbol)
);

我正在尝试制作一个触发器,当用户向行星添加新元素并且该行星中的元素总和超过 100% 时会发出警告。我想出了这个。

CREATE OR REPLACE TRIGGER elem_in_planet_check
    AFTER INSERT OR UPDATE ON elem_in_planet
    FOR EACH ROW
DECLARE
    sum_var NUMBER;
    PRAGMA autonomous_transaction;
BEGIN
    SELECT SUM(percent_representation)
    INTO sum_var
    FROM elem_in_planet
    WHERE id_planet = :NEW.id_planet
    GROUP BY id_planet;

    EXCEPTION
        WHEN NO_DATA_FOUND THEN
            sum_var := 0;

    IF sum_var > 100 THEN
        DBMS_OUTPUT.put_line('WARNING: Blah blah.');
    END IF;
END;
/

这段代码似乎每次都抛出 NO_DATA_FOUND 异常,即使我已经插入了测试数据并且当我单独运行 SQL 查询时,它也能按预期工作。

我是新手,不明白我做错了什么。

感谢您的建议。

【问题讨论】:

  • 与您的问题无关,但您不需要GROUP BY 子句。

标签: sql oracle plsql database-trigger


【解决方案1】:

将该行插入到表中,有 2 个原因。

  1. 触发器作为插入语句的一部分运行,该语句没有 完全的。所以该行不存在。
  2. 您指定了“PRAGMA 自治事务”(又名创建 untraceable bug here 声明),您之前是否得到了变异 表异常。所以你看不到任何插入/更新/删除的数据 通过当前事务。此外,如果确实发生了错误,则仍将插入该行,因为您没有引发错误或重新引发现有错误。我建议您熟悉PLSQL block structure。现在你可能想试试:

您可以使用 after statement 触发器或复合触发器的 after statement 部分进行此测试,如果 sum > 100 则执行 raise_application_error;

顺便说一句,您的“如果 sum_var > 100”仅在发生错误时运行。该块的“例外”之后和 END 之前的任何内容仅在发生错误时运行。

create or replace trigger elem_in_planet_check
    after insert or update on elem_in_planet
declare 
   error_detected boolean := False;
begin
    for planet in 
        (
         select id_planet, sum_var
           from (select id_planet, sum(percent_representation) sum_var
                   from elem_in_planet
                  group by id_planet
                )
         where sum_var > 100
        )
    loop
       dbms_output.put_line('Planet ' || planet.id_planet || ' at '|| planet.sum_var || '% resources exceed 100%');  
       error_detected:= True; 
    end loop; 

    if error_detected then 
            Raise_application_error('-20001', 'Planet resources cannot exceed 100%'); 
    end if;

end elem_in_planet_check;

【讨论】:

  • 我会确保阅读链接页面,谢谢。我尝试使用此代码,现在当我尝试插入超过 100% 时得到此代码:ORA-20001: Planet resources cannot exceed 100% ORA-06512: at "SQL_JOKDWALXEOQAMSQXZEKJUKYGK.ELEM_IN_PLANET_CHECK", line 19 ORA-06512: at "SYS.DBMS_SQL", line 1721。循环中的行根本不打印。还有什么我想念的吗?
  • 你是怎么称呼这个例程的。错误消息是“ORA20001:...”这是触发器生成的消息。 ORA-06512 表明调用例程可能没有处理触发器抛出的异常。使用独立插件运行我没有问题。
  • 我运行了这两个查询:INSERT INTO elem_in_planet VALUES (1, 'Fe', 70); INSERT INTO elem_in_planet VALUES (1, 'O', 99); ID 为 1 的行星和化学元素都存在于各自的表中。在我运行第二个查询后显示了三个错误。 “错误消息在那里“ORA20001:...”那是触发器生成的消息”是的,错误消息在那里,但我希望打印循环语句之间的消息也出来了,出于某种原因它没有。我在 livesql.oracle.com 上运行它,这可能是问题的一部分吗?
  • 看不到消息的原因是因为它要去dbms_output,而不是进入异常消息。确保您已“设置服务器输出”或刚开始放入“dbms_outpit.enable(buffersize => null);”行在实时环境中,该消息将被插入到日志表中 - 使用具有自主事务的过程(这是极少数原因之一),因为 raise_application_error 会回滚所有插入的行,因此不会处理该错误。
  • 我明白了。谢谢你的解释!
猜你喜欢
  • 1970-01-01
  • 2014-03-28
  • 2021-11-16
  • 2014-02-06
  • 1970-01-01
  • 2015-12-31
  • 2011-04-17
  • 2014-03-06
  • 2019-05-24
相关资源
最近更新 更多