Oracle 触发器:SELECT INTO,未找到数据

Oracle trigger: SELECT INTO, no data found

我有以下 table 描述每个行星由哪些化学元素组成的百分比。

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 查询时,它仍按预期工作。

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

感谢您的任何建议。

将行插入 table,有 2 个原因。

  1. 触发器作为插入语句的一部分运行 完全的。所以该行不存在。
  2. 您指定了 "PRAGMA autonomous_transaction"(又名创建 untraceable bug here statement), 你之前有没有得到一个变异 table 异常。所以你看不到任何数据inserted/updated/deleted 由当前 transaction.Further 如果确实发生错误,该行仍将被插入,因为您没有引发错误或重新引发现有错误。我建议你熟悉一下PLSQL block structure。现在你可能想试试:

您可以使用后语句触发器或复合触发器的后语句部分进行此测试,执行 raise_application_error if sum > 100;

顺便说一句,您的 "if on sum_var > 100" 仅在发生错误时运行。该块的 "EXCEPTION" 之后和 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;