触发器避免变异表并更新:new.values

时间:2014-01-21 11:23:14

标签: sql oracle triggers mutating-table

我有一张看起来像这样的小桌子

table People
(  name VARCHAR(20) PRIMARY KEY
   ,group NUMBER(4) 
);

我需要创建触发器(或触发器)以允许以下规则工作:
    - 如果中有超过10个名称,则为1,如果有人试图为此组插入下一个人,则需要引发错误。
    - 2如果INSERT为字段带有NULL值,我需要将其分配给计数小于10的组。
    - 如果所有组中有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;

上面的代码有效,但是当我从镜像表中选择INS例如 INSERT INTO people(name) select concat(name,'_next') from people_mirror;  结果是它超过了组的给定限制(10)。  另外我知道使用 PRAGMA AUTONOMOUS_TRANSACTION 并不是避免变异表错误的最佳方法,我知道如果我将行触发器拆分为语句触发器我可以达到此功能但我只是想法怎么弄它。

所以有人吗? ;)

提前致谢。

------------------- EDIT ------------------------

这些是有效的触发器,但我仍然对它们有疑问,因为它们都是BEFORE和行类型。

CREATE OR REPLACE TRIGGER people_bir1
BEFORE INSERT on people
FOR EACH ROW
DECLARE 
V_count NUMBER(2);
BEGIN
   If :NEW.group = '' or :NEW.group is null then
   return;
   end if;
insert into groups values(:New.group,1);
exception when dup_val_on_index then
Select count into v_count from groups where group = :New.group;
UPDATE groups set count = v_count+1 where group = :New.group;

END people_bir1;      

CREATE OR REPLACE TRIGGER people_bir2
BEFORE INSERT on people
FOR EACH ROW
DECLARE
g_count NUMBER(2);
g_num NUMBER(2);
begin
   if :NEW.group = '' or :NEW.group is null Then
   select min(count) into g_count from groups;
       if g_count = 10 Then
       select max(group) into g_num from groups;
       g_num := g_num+1;
       Else
       select min(group) into g_num from group where count = g_count;
       End if;
   :New.group := g_num;
   Else
   select count into g_count from groups where group=:New.group;
   if g_count > 9 then 
   raise_application_error (-20003,'More then 10 people in group please select another');
   end if;
   end if;
end people_bir2;

因为它太长了我无法将其粘贴为对@TonyAndrews答案的评论。

2 个答案:

答案 0 :(得分:2)

你是对的,添加PRAGMA AUTONOMOUS_TRANSACTION是没有办法解决的。一种方法是在GROUPS表中维护每个组的人数(如果你没有GROUPS表,那么你可以添加一个)使用PEOPLE上的触发器:

  • 在PEOPLE上INSERT之后:更新GROUPS,将其添加1到
  • 中的组计数
  • 在PEOPLE上删除后:更新GROUPS,从他们所在的群组中扣除1
  • 在PEOPLE上更新后:更新GROUPS,将1添加到新组,从旧组中减去1

然后你的BEFORE INSERT触发器不需要查看PEOPLE表,它可以查看GROUPS:

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

请注意for update子句锁定GROUPS行,直到您的交易完成。

答案 1 :(得分:0)

您可以使用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;

请注意,很可能此代码无法正常工作,但它应该让您对如何使用复合触发器有一个总体印象。