假设有两个表
1.student(roll_no, class, credits)
2.class(class, total_student, Total_credits)
如何使用学生表上的光标创建触发器,该触发器将使用学生总数及其总学分更新班级?在每次插入或删除时
表的更新total_student
和total_credit
列将涉及在表上定义的触发器中class
针对表编写查询。这样做会导致ORA-04091: table name is mutating, trigger/function may not see it错误。为避免该错误,至少有三种方法可以在 每次表中发生更改(删除/插入/更新)时获取和更新。假设 master( )/detail( ) 关系中的表:student
student
total_student
total_credits
student
class
student
第一种方法(最大的)将涉及创建几个数据库对象:
嵌套表 SQL 类型
create or replace type T_clasids is table of number;
将具有该 SQL 类型的变量来存储类ID
s 的包。不知何故受到了 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;
结尾;
三个触发器: 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
第二种方法(最短的一种和个人首选的一种)是从表中删除total_student
并创建一个视图,该视图将计算并保持有关班级学生总数和学分总和的最新信息:total_credits
class
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
第三种方法。在表上定义一个插入/更新/删除语句级触发器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
声明的更多信息。