在插入新记录之前触发检查部门的平均工资

时间:2017-10-06 02:13:09

标签: oracle plsql triggers mutation

让我们说员工表如下

 EID    ENAME   DEPTNO  SALARY
 1      john    10      100
 2      jau     10      300
 3      cau     10      200
 4      cha     20      200
 5      cwea    20      500
 6      dan     20      200
 7      an      20      300

我必须检查是否添加了新员工,新员工薪水应该大于该部门的平均薪水,这应该在触发器中完成。

所以我创建了触发器,如下所示

create or replace trigger tg_emp  before insert on employee for each row  
declare 
avgsal number; 
highsalary EXCEPTION; 
BEGIN   
select avg(salary) into avgsal from employee where deptno = :NEW.deptno; 
if :NEW.salary < avgsal 
then   
raise highsalary; 
end if; 
EXCEPTION 
when highsalary then
Raise_Application_Error (-20343, 'salary is less than the avg salary in this 
department');  
WHEN others THEN  
Raise_Application_Error (-20353, 'other error probably table mutation 
error');  
END; 

如您所知,此代码仅适用于下面的单个插入

insert into employee values (8, 'jj', 10, 500);

但如果它是一次多次插入,如

insert into employee
select seq_emp.next, 'ffgg', 10, 400 from all_tab_columns where rownum < 5;

它会引发表突变错误(我知道上面的插入没有意义,但我在一个语句中只将其用作多插入的示例)。

那么我们如何使用全局临时表来解决这个问题呢?

我想我能够使用1 GTT和1之前的语句触发器和1之前的行触发器来解决它,如下所示

CREATE GLOBAL TEMPORARY TABLE employee_GTT (
id           NUMBER,
name  VARCHAR2(20),
deptno number,
salary number
)
ON COMMIT DELETE ROWS;

触发前的语句级别

create or replace trigger emp_avg_load  before insert on employee 
begin
insert into dept_avg
select deptno, avg(salary), count(deptno) from employee group by deptno;
dbms_output.put_line('getting data from GTT');
end;

触发前的行级

create or replace trigger tg_emp  before insert on employee for each row  
declare 
avgsal number;
ct number;
highsalary EXCEPTION; 
BEGIN  
avgsal := :new.salary; 
select avgsal, count into avgsal, ct from dept_avg where deptno = 
:NEW.deptno;
if :NEW.salary < avgsal 
then   
raise highsalary; 
else
update dept_avg
set count = count +1,
avgsal = (avgsal+:NEW.salary)/(count+1)
where deptno = :NEW.deptno;
 end if; 
EXCEPTION 
when highsalary then
Raise_Application_Error (-20343, 'salary is less than the avg salary in this 
department');  
WHEN others THEN  
Raise_Application_Error (-21343, 'some other error'); 
END;

如果我弄错了,请纠正我。

1 个答案:

答案 0 :(得分:0)

首先使用临时表的方式是个好主意。

但是,您在交易期间更新平均工资 的方式 会向我显示 错误 。实际上,根据Oracle如何处理更新(插入顺序),您将得不到相同的结果。

  • 首先,由于您只在工资高于平均水平时插入员工,因此平均值只会增加。
  • 现在,如果先插入最高工资,那么下一个员工的平均成绩可能会增加太多。

您遇到困难,因为您的要求不够明确。

我不认为在插入行时更改平均工资是真实的用例。您必须在事务级别考虑它:因此,平均工资在事务发生之前定义为。因此,在插入时无需进行平均更改。只是让交易期间的平均值保持不变。

我会删除此部分:

else
  update dept_avg
     set count = count +1
       , avgsal = (avgsal+:NEW.salary)/(count+1)
  where deptno = :NEW.deptno;