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

Posted

技术标签:

【中文标题】在插入新记录之前触发检查部门的平均工资【英文标题】:trigger to check avg salary of the department before inserting new record 【发布时间】:2017-10-06 02:13:09 【问题描述】:

假设员工表如下

 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;

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

【问题讨论】:

你确定执行单行插入它有效吗?您不能在使用触发器的同一个表上进行查询。您必须使用复合触发器。无论如何,为了让您在同一张表上没有那么多触发器,我建议您创建一个复合触发器。一个快速的谷歌将帮助你在 sintax 上。但是,如果您需要帮助,请大声喊叫。 是的,执行单行插入将起作用,这是变异表错误的例外。我会寻找复合触发器。谢谢你告诉我。 【参考方案1】:

你使用临时表的方式一开始是个好主意。

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

首先,由于您仅在薪水高于平均水平时才插入员工,因此平均只能增加。 现在,如果首先插入最高薪水,那么对于要插入的下一个员工来说,平均值可能会增加太多。

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

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


我会删除这部分:

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

【讨论】:

实际上 GTT 必须更新,因为在单个插入语句中,我可以将多个插入到同一个部门下面是一个示例。插入员工 select 8, 'jj', 10, 600 from dual union select 9, 'jgh', 10, 250 from dual 在这些插入之前 10 部门的平均为 200,首先插入满足 avgsal 条件,但现在如果我不更新 GTT 则第二个插入也满足条件,但是如果 GTT 使用第一个插入更新,则第 10 个部门的 avgsal 将为 300,因此第二个插入将失败。 我只是不会使用触发器来做你想做的事。过于复杂。而且要求似乎不合逻辑。您可以使用 PL SQL 包更轻松地处理此问题。

以上是关于在插入新记录之前触发检查部门的平均工资的主要内容,如果未能解决你的问题,请参考以下文章

查询各部门中高于部门平均工资的人员,人数及该部门的平均工资

SQL数据库 计算出每个部门的平均工资 最高工资和最低工资 语法怎么写?

SQL - 试图让员工的平均工资高于平均部门工资

写下SQL显示员工(工资大于5000)平均工资小于8000的部门

数据库题——高于部门平均工资查询问题

SQL用他们部门的平均工资更新员工的工资