更新触发器PL / SQL Oracle

时间:2016-05-22 09:01:59

标签: plsql oracle11g triggers

我有一个具有以下结构的表:

create table treballa (
code varchar2(4), 
 name varchar2(20), 
 director varchar2(4), 
department number, 
salary int,
 primary key (code), 
 foreign key (director) references treballa(code) 
) 

我需要创建一个触发器,检查更新后的工作人员的部门编号,该部门所有工人之间的总工资是否超过10000,但我不知道该怎么做真的......你能帮我吗?非常感谢你。

编辑:

CREATE OR REPLACE TRIGGER controlsalaridept BEFORE INSERT OR UPDATE ON TREBALLA
FOR EACH ROW
  DECLARE
    salaritotal INT := 0;
  BEGIN
    IF INSERTING THEN
      SELECT sum(salary) INTO salaritotal FROM TREBALLA WHERE DEPARTMENT LIKE :new.DEPARTAMENT;
      DBMS_OUTPUT.PUT_LINE('Salari Total abans suma:'||salaritotal);
      salaritotal := salaritotal + :new.SALARY;
      DBMS_OUTPUT.PUT_LINE('Salari Total després:'||salaritotal);
      IF salaritotal > 10000 THEN
        raise_application_error(-20025, 'La suma del salari total de cada departament supera els 10000 Euros');
      END IF;
    END IF;
    IF UPDATING THEN
      SELECT sum(salary) INTO salaritotal FROM TREBALLA WHERE DEPARTMENT LIKE :old.DEPARTAMENT;
      DBMS_OUTPUT.PUT_LINE('Salari Total abans suma:'||salaritotal);
      salaritotal := salaritotal - :old.SALARY + :new.SALARY;
      DBMS_OUTPUT.PUT_LINE('Salari Total després:'||salaritotal);
      IF salaritotal > 10000 THEN
        raise_application_error(-20026,'La suma del salari total de cada departament supera els 10000 Euros');
      END IF;
    END IF;
  END;

结束编辑/

更新错误:

  

[42000] [4091] ORA-04091:表SPECIAL.TREBALLA正在变异,   触发/功能可能看不到它ORA-06512:at   " SPECIAL.CONTROLSALARIDEPT",第14行ORA-04088:执行期间出错   触发器' SPECIAL.CONTROLSALARIDEPT'

PD:对不起,我在甲骨文上很新,我需要帮助解决这个问题,我不知道即使它做对了我做的事......触发器的第一部分" IF INSERTING"效果很好,问题出在UPDATING ......

1 个答案:

答案 0 :(得分:1)

尝试复合触发器:

CREATE OR REPLACE TRIGGER compound_trigger_name
FOR  INSERT OR UPDATE OF salary ON treballa
COMPOUND TRIGGER

  TYPE Departments_t   IS TABLE OF treballa.department%TYPE INDEX BY varchar2(100);
  Departments          Departments_t;

     BEFORE EACH ROW IS
     BEGIN
        -- collect updated or inserted departments 
        Departments( :new.department ) := :new.department;
     END BEFORE EACH ROW;

     AFTER STATEMENT IS
        sum_sal NUMBER;
     BEGIN
      -- for each updated department check the restriction
      FOR dept IN Departments.FIRST .. Departments.LAST
      LOOP
         SELECT sum(salary) INTO sum_sal FROM treballa WHERE department = dept;
         IF sum_sal > 1000 THEN
            raise_application_error(-20123, 'The total salary for department '||dept||' cannot exceed 1000');
         END IF;
      END LOOP;
     END AFTER STATEMENT;

END compound_trigger_name;
/

========编辑 - 一些问题和答案===========

问:为什么会发生变异表错误?
答:这在文档中有描述:
http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#g1699708

  

对变异表的触发限制
  变异表是一个表   正在被UPDATE,DELETE或INSERT语句或a修改   可能由DELETE CASCADE的效果更新的表   约束

     

发出触发语句的会话无法查询或   修改变异表。此限制可防止触发器发生   看到一组不一致的数据。

     

此限制适用于使用FOR EACH ROW的所有触发器   条款。不考虑在INSTEAD OF触发器中修改的视图   突变。

     

当触发器遇到变异表时,会发生运行时错误,   滚动触发器主体和触发语句的效果   返回,并将控制权返回给用户或应用程序。 (您可以使用   复合触发器以避免变异表错误。更多   信息,请参阅使用复合触发器来避免变异表   错误。)

问:如何避免变异表错误?
答:文档建议使用coumpound触发器,请参阅:http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#CHDFEBFJ

  

使用复合触发器来避免变异表错误您可以使用   复合触发器以避免变异表错误(ORA-04091)   在变异表的触发限制中描述。

问:什么是复合触发器?它是如何工作的?
答:这是一个很大的话题,请参考此处的文档:http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#CIHEFGFD

简而言之:这是一种特殊的触发器,可以将四种类型的单独触发器组合起来:BEFORE statementBEFORE-for each rowAFTER for each rowAFTER statament到一个声明中。它可以更容易地实现一些需要将一些数据从一个触发器传递到另一个触发器的情况。请查看以上链接了解更多详情。

问:但实际上"Departments( :new.department ) := :new.department;是什么?
答:此声明将部门编号存储到关联数组中。

此数组在复合触发器的声明部分中声明:

  TYPE Departments_t   IS TABLE OF treballa.department%TYPE INDEX BY varchar2(100);
  Departments          Departments_t;

与复合触发器相关的文档说:http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#CIHJBEFE

  

可选的声明部分(第一部分)声明变量和   定时点部分可以使用的子程序。触发时   触发,声明部分在任何定时点之前执行   执行。本节中声明的变量和子程序具有   发射声明持续时间。

上述意味着Departments变量仅在整个处理开始时初始化一次,就在触发器触发后。 "射击声明持续时间"表示在触发器完成后销毁此变量。

此语句:Departments( :new.department ) := :new.department;在关联数组中存储部门号。它位于BEFORE EACH ROW部分,然后由更新/插入语句更新(或插入)的每一行执行。

:new:old是伪记录,您可以在此处找到更多内容:http://docs.oracle.com/cd/E11882_01/appdev.112/e25519/triggers.htm#LNPLS99955简而言之::new.department检索department列的新值 - 对于当前更新的行(更新值 - 更新后),而:old.department给出该列的旧值(在更新之前)。

此集合稍后在AFTER STATEMENT中使用,当trigers选择所有更新的部门(在FOR-LOOP中)时,每个部门火灾SELECT SUM(salary) ...,然后检查此总和是否小于1000

考虑一个简单的更新:UPDATE treballa SET salary = salary + 10。这是一个单独的更新语句,但一次更改了许多行。触发器的执行顺序如下:

  1. 触发更新暂停:UPDATE treballa SET salary = salary + 10
  2. 执行触发器的声明部分,即:Departments变量已初始化
  3. 对于每个更新的行,分别执行
  4. BEFORE EACH ROW部分 - 与要更新的行一样多次。在这个地方,我们从更改的行中收集所有部门。
  5. AFTER STATEMENT部分已执行。此时表已经更新 - 所有行都已有新的更新工资。我们循环保存在Departments中的部门,并且对于每个部门,我们检查工资总和是否小于或等于1000.如果这个总和是>对于任何这些部门,将引发1000错误,然后抛出错误,并中止并回滚整个更新。否则触发器完成,并且更新完成(但您仍需要提交这些更改)。
  6. 问:什么是关联数组,为什么只使用这种集合而不是其他集合(varray或嵌套表)?
    答:PL / SQL集合是一个很大的话题。请点击此链接了解相关内容:http://docs.oracle.com/cd/E11882_01/appdev.112/e25519/composites.htm#LNPLS005

    简而言之 - 关联数组(或索引表)就像java中的映射(hashmap,treemap等) - 它是一组键值对,每个键都是唯一的。您可以多次将相同的键放入此数组(具有不同的值),但此键只存储一次 - 它是唯一的。
    我用它来获得独特的部门。
    再次考虑我们的更新示例:UPDATE treballa SET salary = salary + 10 - 此命令触及数百个具有相同部门的行。我不想要将相同部门的集合重复100次,我需要一组独特的部门,而且我希望每个部门只执行一次查询SELECT sum()...,而不是100次。在sssociative数组的帮助下,它会自动完成 - 我得到了一组独特的deparments。