1

假设有两个表

 1.student(roll_no, class, credits)
 2.class(class, total_student, Total_credits)

如何使用学生表上的光标创建触发器,该触发器将使用学生总数及其学分更新班级?在每次插入或删除时

4

1 回答 1

1

表的更新total_studenttotal_credit列将涉及在表上定义的触发器中class针对表编写查询。这样做会导致ORA-04091: table name is mutating, trigger/function may not see it错误。为避免该错误,至少有三种方法可以在 每次表中发生更改(删除/插入/更新)时获取和更新。假设 master( )/detail( ) 关系中的表:studentstudenttotal_studenttotal_credits studentclassstudent

  1. 第一种方法(最大的)将涉及创建几个数据库对象:

    1. 嵌套表 SQL 类型

      create or replace type T_clasids is table of number;
      
    2. 将具有该 SQL 类型的变量来存储类IDs 的包。不知何故受到了 DML 语句的影响。

      包装规格:

      create or replace package Pkg
      is
        procedure reset_list_of_ids;      -- empties out the list of class ids
        procedure add_id(p_id in number); -- add new class id to a list if row
                                          -- with this ID has been affected by  
                                          -- a DML statement
        procedure update_class;           -- updates class table
      end;
      

      包体:

      create or replace package body PKG
      is
      
         g_classids T_clasids := T_clasids();
      
      procedure reset_list_of_ids
      is
      begin
        g_classids.delete;
      end;
      
      procedure add_id(p_id in number)
      is
      begin
        g_classids.extend;
        g_classids(g_classids.count) := p_id;
      end;
      
      procedure update_class
      is
      begin
        update class t
           set (  t.total_student
                , t.total_credits ) = ( select count(*)
                                             , sum(s.credits)
                                          from student s
                                         where s.class = t.class)
         where t.class in (select column_value
                             from table(g_classids));
      
       end;
      

      结尾;

    3. 三个触发器: a) Before 语句;b) 排后;c) 声明之后。

      -- before insert/update/delete statement level trigger 
      -- to empty out the class id list
      create or replace trigger tr_bs_initialize
      before insert or delete or update on student
      begin
        pkg.reset_list_of_ids;
      end;
      
      
      
      -- after insert/update/delete statement level trigger
      -- to update class table with new information 
      create or replace trigger tr_as_update_class
      after insert or delete or update on student
      begin
        pkg.update_class;
      end;
      
      
      -- after insert/update/delete row level trigger
      -- to populate class id collection with ids of 
      -- rows which has been affected by a DML statement
      create or replace trigger tr_ar_populate
      after insert or delete or update on student
      for each row
      begin
         -- nvl(:new.class, :old.class)
         -- in a case :new.clas happens to be null
         pkg.add_id(nvl(:new.class, :old.class));
      end;
      

      这是它如何工作的示例:

      select t.* from class t;
      
      CLASS      TOTAL_STUDENT TOTAL_CREDITS
      ---------- ------------- -------------
       1         null          null     
       2         null          null 
       3         null          null
      
       insert into student(roll_no, class, credits)
          values(1, 2, 3);
      
      
       select t.* from class t;
      
       CLASS      TOTAL_STUDENT TOTAL_CREDITS
       ---------- ------------- -------------
       1             null          null 
       2             1             3
       3             null          null
      
  2. 第二种方法(最短的一种和个人首选的一种)是从表中删除total_student并创建一个视图,该视图将计算并保持有关班级学生总数和学分总和的最新信息:total_creditsclass

        create or replace view v_class as
           select c.class
                , count(s.class) as total_students
                , sum(s.credits) as total_credits
             from student s
            right join class c
               on (c.class = s.class)
            group by c.class
    
         select t.* from v_class t;
    
         CLASS      TOTAL_STUDENTS TOTAL_CREDITS
         ---------- -------------  -------------
         1             null          null 
         2             1             3
         3             null          null
    
  3. 第三种方法。在表上定义一个插入/更新/删除语句级触发器sudent并使用merge语句更新class表:

         create or replace trigger tr_aiudsl
         after insert or update or delete on student
         begin
            merge into class c
            using (select t.class
                        , count(*)      as total_students
                        , sum(t.credits)as total_credit
                     from student t
                    group by t.class) q
                       on (q.class = c.class)
             when matched
             then update
               set c.total_student = q.total_students
                 , c.total_credits  = q.total_credit;
         end;
    

    了解有关merge声明的更多信息。

于 2013-09-02T19:28:41.517 回答