【问题标题】:How do i resolve this trigger in pl/sql?如何在 pl/sql 中解决此触发器?
【发布时间】:2020-05-05 11:20:27
【问题描述】:

我想要一个触发器,一个给定项目的工作人员不超过 3 人 .

create or replace trigger t_maxim_empleats_projecte
before insert or update of codi_proj
on empleats
for each row
declare
contador number(5);
begin
select count(codi_proj) into contador from empleats where codi_proj= :new.codi_proj;
if contador > 3 then
RAISE_APPLICATION_ERROR(-20000, 'No poden haver-hi més de 3 empleats en un
mateix projecte.');
end if;
end;

这就是我的 SQL 表中的全部内容:

create table Projectes(
    codi_proj   number(5), 
    nom_proj    varchar2(25),
    pressupost  number(10,2),
    primary key (codi_proj)
);

create table Empleats(
    codi_emp    number(5),
    nom_emp varchar2(15),
    sou     number(10,2),
    codi_dept   number(5),
    codi_proj   number(5),
    data_alta   date,
    primary key (codi_emp),
        foreign key (codi_dept)   references Departaments(codi_dept) on delete set null,
        foreign key (codi_proj)   references Projectes(codi_proj) on delete set null
);

insert into projectes(codi_proj, nom_proj, pressupost)
values (1, 'Daisy', 240000);

insert into projectes(codi_proj, nom_proj, pressupost)
values (2, 'CLAM', 63000);

insert into projectes(codi_proj, nom_proj, pressupost)
values (3, 'Vocal Processor', 600000);

insert into empleats(codi_emp, nom_emp, sou, codi_dept, codi_proj,data_alta)
values (1, 'Maria', 21000, 1, 1,TO_DATE('10/10/1980','dd/mm/yyyy'));

insert into empleats(codi_emp, nom_emp, sou, codi_dept, codi_proj,data_alta)
values (2, 'Josep', 18000, 1, 1,TO_DATE('01/08/1982','dd/mm/yyyy'));

insert into empleats(codi_emp, nom_emp, sou, codi_dept, codi_proj,data_alta)
values (3, 'Ramon', 48000, 4, 2,TO_DATE('05/04/2005','dd/mm/yyyy'));

【问题讨论】:

  • “有时有效,有时无效”。您需要使用受控数据集运行一些测试。一旦您了解了表格的起始状态,您就可以调查哪些输入有效,哪些无效,然后您应该了解如何解决您的问题。此外,如果您只是说“有时有效,有时无效”,对我们帮助不大。您需要描述它是如何不起作用的:会发生什么?你得到一个你没想到的错误吗?还是没有收到您希望的错误?
  • 您可能会发现this answer to a previous question 提供了一些见解。
  • 请查看答案和反馈。

标签: sql oracle plsql database-trigger


【解决方案1】:

至少使用当前的行级触发器方法,您不会做您想做的事。 原因是行级触发器无法访问导致其触发的表。结果,语句“select ... from empleats ...将抛出异常”ORA-04091:表正在变异,触发器/函数可能看不到它”,因为触发器是为了响应针对表 empleats 的 DML 而触发的。 一个更好的过程是在业务规则层,甚至是应用程序层进行这种检查。但是,如果您坚持使用触发器,则可以通过 after 语句 触发器来完成。该触发器确实必须(或至少应该)处理超过最大员工限制的多个项目的可能性。 所以(见fiddle):

create or replace trigger limit_3_emp_per_proj_ais
           after insert on employees 
declare
   k_new_msg_line    constant            varchar2(3)  := chr(10) || '  ';
   k_max_emp_message constant            varchar2(80) := 
       'No rows inserted!' || k_new_msg_line || 'Following Projects have exceed max of 3 employees:'; 
   cursor proj_over_3_emp is
         select proj.name, proj.proj_id, count(*)
           from projects  proj 
           join employees emp
             on (emp.proj_id = proj.proj_id)
          group by proj.proj_id, proj.name    
         having count(*) > 3 
          order by proj.proj_id;

   l_proj_exceeds_3_emp                varchar2(3500) := null;           
begin 
   for proj_emp in proj_over_3_emp  
   loop 
       l_proj_exceeds_3_emp := l_proj_exceeds_3_emp || k_new_msg_line ||
                               proj_emp.name || '(' || proj_emp.proj_id || ')';
   end loop; 

   if l_proj_exceeds_3_emp is not null
   then 
      raise_application_error( -20199,k_max_emp_message || l_proj_exceeds_3_emp);
   end if;

end limit_3_emp_per_proj_ais;

【讨论】:

    【解决方案2】:

    有两个问题。

    问题 1:我猜您希望触发器在 count(codi_proj) 大于 3 时触发。当您插入第 4 条记录时,触发器不会触发,因为触发器在“插入前”触发,这意味着它得到计为 3 并且不会考虑插入当前记录。因此,要修复它,您可以修改触发器以执行大于或等于 contador >= 3 的代码。

    问题 2:测试方法还有另一个问题。如果您通过将记录插入到项目中来测试触发器,如帖子中所述,您必须在每次插入(或 DML)操作后提交。触发器像任何其他数据库对象从数据的持久状态中读取。

    【讨论】:

    • 您的问题 2 不太正确;每个 DML 都不需要提交。虽然确实没有其他事务可以看到未提交的更改,但发布事务可以看到它已发布的 DML 的结果,包括触发器中采取的任何操作(当前正在处理的行除外)。见Fiddle。没有提交,但选择确实显示表更改。实际上在每个 DML 之后提交是不好的做法。
    • @Belayer - “每个 DML 都不需要提交。” - 提到提交的原因是在现实世界(或产品系统)OLTP 应用程序 GUI 中,用户创建的每个会话都将执行插入,然后提交以实现正流。从同一会话插入不会模仿现实世界的场景。我同意任何会话的持久状态对象意味着所有已提交的会话,该会话对相关对象执行 DML + 当前会话中未提交的 DML。
    • 我猜这是工具和设计的问题。在现实世界中,我总是选择工具和设计,以便事务可能包含许多要完成的 DML 语句。我的反对意见是“您必须在每次插入后提交”这句话在单个事务中是不正确的。
    【解决方案3】:

    感谢您的帮助,我已经部分解决了这个问题。但是,当我插入它时,它可以工作,但是当我更新它时,当我在同一个项目中有超过 3 个工作人员时,它就不起作用了。 我认为它不起作用,因为升级需要一个运行另一个过程的过程

    【讨论】:

    • 不需要其他程序。您是否更改了我发送的触发事件作为仅在“插入员工后”插入时触发的版本。包括更新更改为“在员工插入或更新后”。不幸的是,不能将语句级别设置为仅在更新特定列时触发。
    猜你喜欢
    • 1970-01-01
    • 2020-12-30
    • 1970-01-01
    • 1970-01-01
    • 2023-03-09
    • 2011-08-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多