Sql 避免表突变和更新的触发器:new.values

Sql 避免表突变和更新的触发器:new.values,sql,oracle,triggers,mutating-table,Sql,Oracle,Triggers,Mutating Table,我有一张像这样的小桌子 table People ( name VARCHAR(20) PRIMARY KEY ,group NUMBER(4) ); 我需要创建一个或多个触发器,允许以下规则工作: -1如果组中有超过10个姓名,如果有人试图为该组插入下一个姓名,我需要提出一个错误。 -2如果INSERT为group字段提供空值,我需要将其分配给计数小于10的组。 -3如果所有组中有10个名称,我需要生成下一个组号。 -4我需要避免变异表错误 这就是我到目前为止所做的: CREATE

我有一张像这样的小桌子

table People
(  name VARCHAR(20) PRIMARY KEY
   ,group NUMBER(4) 
);
我需要创建一个或多个触发器,允许以下规则工作: -1如果组中有超过10个姓名,如果有人试图为该组插入下一个姓名,我需要提出一个错误。 -2如果INSERT为group字段提供空值,我需要将其分配给计数小于10的组。 -3如果所有组中有10个名称,我需要生成下一个组号。 -4我需要避免变异表错误

这就是我到目前为止所做的:

CREATE OR REPLACE TRIGGER people_bis
BEFORE INSERT ON people
FOR EACH ROW
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
g_count NUMBER(4);
g_num NUMBER(4);
g_l NUMBER(4);
g_r NUMBER(4);

BEGIN

Select count(*) into g_count from people where group = :new.group;
If g_count > 9 Then
raise_application_error (-20003,'Group reached it limit, please choose other');
End if;
If :NEW.group = '' or :NEW.group is null Then
 select count (*) into g_l from (select count(imie),group from people group by group having count(name) = 10);
select count (distinct group) into g_r from people;
    if g_l = g_r then
    select max(group)+1 into g_num from people;
    else
    select group into g_num from(select group, count(name) from people having  count(name) < 10 group by group order by count(group) desc) where rownum < 2;
    End if;
:New.group := g_num;
End if;
End people_bis;
由于太长,我无法将其粘贴为@TonyAndrews answer的注释。

添加PRAGMA autonomy_事务不是解决方案,你是对的。一种方法是在“组”表中维护每个组的人数。如果没有“组”表,则可以使用“人”上的触发器添加一个:

在插入“人员:更新组”后,将1添加到他们所在的组的计数中 删除“人员:更新组”后,从他们所在的组的计数中减去1 更新人员后:更新组,向新组添加1,从旧组中减去1 然后,插入前触发器不需要查看PEOPLE表,它可以查看组:

Select people_count into g_count from groups where group = :new.group
for update;

注意for update子句将锁定GROUPS行,直到事务完成。

您可以使用compund触发器。看起来是这样的:

CREATE OR REPLACE TRIGGER people_bis
   FOR INSERT ON people
COMPOUND TRIGGER

g_count NUMBER(4);
g_num NUMBER(4);
g_l NUMBER(4);
g_r NUMBER(4);

BEFORE STATEMENT IS
BEGIN

   Select count(*) into g_count from people where group = :new.group;
   If g_count > 9 Then
   raise_application_error (-20003,'Group reached it limit, please choose other');
   End if;

   select count (*) into g_l from (select count(imie),group from people group by group     having count(name) = 10);
   select count (distinct group) into g_r from people;

    if g_l = g_r then
    select max(group)+1 into g_num from people;
    else
    select group into g_num from(select group, count(name) from people having  count(name) < 10 group by group order by count(group) desc) where rownum < 2;
    End if;

END BEFORE STATEMENT;

BEFORE EACH ROW IS
BEGIN

   If :NEW.group = '' or :NEW.group is null Then
   :New.group := g_num;
   End if;
END BEFORE EACH ROW;

End people_bis;

请注意,最有可能的是,这段代码没有按照您的要求工作,但它应该会给您一个如何使用复合触发器的总体印象。

谢谢您的建议,但我没有提到,对于此任务,我不能使用复合触发器:/因此,我能够使用其他表组组编号4主键将其拆分为两个触发器,计数数字2和两个触发器,但它们都在行触发器之前。这个函数可以工作,但我怀疑使用两个before触发器是否是一个合适的解决方案。我在下面附上它们作为答案。如果可以的话,请看一看,纠正我的错误。
CREATE OR REPLACE TRIGGER people_bis
   FOR INSERT ON people
COMPOUND TRIGGER

g_count NUMBER(4);
g_num NUMBER(4);
g_l NUMBER(4);
g_r NUMBER(4);

BEFORE STATEMENT IS
BEGIN

   Select count(*) into g_count from people where group = :new.group;
   If g_count > 9 Then
   raise_application_error (-20003,'Group reached it limit, please choose other');
   End if;

   select count (*) into g_l from (select count(imie),group from people group by group     having count(name) = 10);
   select count (distinct group) into g_r from people;

    if g_l = g_r then
    select max(group)+1 into g_num from people;
    else
    select group into g_num from(select group, count(name) from people having  count(name) < 10 group by group order by count(group) desc) where rownum < 2;
    End if;

END BEFORE STATEMENT;

BEFORE EACH ROW IS
BEGIN

   If :NEW.group = '' or :NEW.group is null Then
   :New.group := g_num;
   End if;
END BEFORE EACH ROW;

End people_bis;