1

assuming two table

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

how to create trigger using cursor on student table which will update class with Total no of Student's and their total credits ? on each insertion or deletion

3
  • Any special reason why you would want to use trigger to get total of students and total credits? Commented Sep 2, 2013 at 18:03
  • Have you tried creating one? If so, what didn't work? Commented Sep 2, 2013 at 18:24
  • Have a look at this, might be helpful. stackoverflow.com/questions/11580177/cursor-in-a-trigger Commented Sep 2, 2013 at 18:50

1 Answer 1

1

Updating of total_student and total_credit columns of the class table will involve writing a query against student table in a trigger defined on student table. Doing so will lead to ORA-04091: table name is mutating, trigger/function may not see it error. To avoid that error there are at least three approaches to get the total_student and total_credits updated every time something is changing(delete/insert/update) in the student table. Assumption has been made that the table in the master(class)/detail(student) relationship:

  1. First approach(largest one) will involve creation of several database objects:

    1. Nested table SQL type

      create or replace type T_clasids is table of number;
      
    2. Package which will have a variable of that SQL type to store class IDs. that somehow has been affected by a DML statement.

      Package specification:

      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;
      

      Package body:

      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;
      

      end;

    3. Three triggers: a) Before statement; b) after row; c) after statement.

      -- 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;
      

      Here is an example of how it works:

      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. Second approach(shortest one and personally preferable one ) is to remove total_student and total_credits from the class table, create a view which will calculate and keep up to date information about total number of student in a class and sum of their credits:

        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. Third approach. Define one after insert/update/delete statement level trigger on the sudent table and use merge statement to update the class table:

         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;
    

    Find out more about merge statement.

Sign up to request clarification or add additional context in comments.

3 Comments

+One for such detailed explanation. I have one doubt though, approach three doesn't raise a mutating trigger or if merge is used mutating trigger can be avoided?
@Polppan In the third approach we use after statement(not row) level trigger. That kind of triggers do not raise mutating table error - data is accessed by the time they already modified
3rd approach is preferable by our teachers, but we have to only use cursor and trigger view is not allowed in this assignment. but thanks allot its a really brief description for me and my friends :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.