首页 > Linux操作系统 > Linux操作系统 > oracle变异表触发器相关问题
CREATE OR REPLACE TRIGGER TRI_KCYXZ1_SFJCHZ
BEFORE update or insert on KCYXZ1
for each row
declare PJLX varchar2(1);
begin
PJLX:=:NEW.KCYXZ1_PJLX;
IF (PJLX='M') THEN
UPDATE KCYXZ1 SET
KCYXZ1.KCYXZ1_SFJCHZ='0'
WHERE KCYXZ1_LSBH=:NEW.KCYXZ1_LSBH;
END IF;
end;
这个触发器,在oracle中compile是没有问题的,但是前台软件使用此表(KCYXZ1)对应的功能的时候,报错(用sql monitor跟踪如下):
----------------------------------
Timestamp: 09:01:38.265
update KCYXZ1 SET KCYXZ1_LSBH =KCYXZ1_LSBH WHERE KCYXZ1_LSBH =:1
:1 = '1053'
Runtime error occurred: 4091 (ORA-04091: 表 LC0039999.KCYXZ1 发生了变化, 触发器/函数不能读它
ORA-06512: 在 "LC0039999.TRI_KCYXZ1_SFJCHZ", line 7
ORA-04088: 触发器 'LC0039999.TRI_KCYXZ1_SFJCHZ' 执行过程中出错)
----------------------------------
从google.com上看了一下这个问题,发现这个是属于变异表(mutating table)的触发器的问题,详细见如下帖子
http://blog.oracle.com.cn/html/58/t-120858.html
此贴子中有如下的描述,我感觉比较有意义,摘录下来:
触发器中的SQL语句不能:
读取或更新触发语句的任何变异表也包括触发表本身,读取或更新该触发表的约束表的主键列、唯一性键列或外键列但是如果需要也可以更新其他列。这些限制约束适用于所有行级触发器。但有个特例就是如果INSERT只影响一行记录那么定义在这行上的行级BEFORE和AFTER触发器就不会将这个触发表当作变异表。
有另外一个帖子说到:
触发器可以包括任何合法的PL/sQL语句,假有以下例外
1.触发器不可以执行COMMIT、ROLLBACK或SAVEPOINT语句,而且不可以调用执行这些语句之一的函数或过程。
2.触发器不可以声明long或LONG RAW变量。
3.触发器不可以在定义它的表上执行DML操作。
我自己写的这个触发器恰恰就违反了"3.触发器不可以在定义它的表上执行DML操作"这个条件.
于是咨询了一下开发工程师,并且在http://www.cnblogs.com/luyongqun/archive/2007/11/19/964470.html帖子的启发下,采用直接赋值的方式,避免了使用update 等dml语句,巧妙的达到了目的.
改后的触发器为(粗体蓝字的为修改了的语句):
CREATE OR REPLACE TRIGGER TRI_KCYXZ1_SFJCHZ
BEFORE update or insert on KCYXZ1
for each row
declare PJLX varchar2(1);
begin
PJLX:=:NEW.KCYXZ1_PJLX;
IF (PJLX='M') THEN
:NEW.KCYXZ1_SFJCHZ:='0';
END IF;
end;
不过估计没有 ;变异表跟约束表问题
书上有以下一句话:在对其执行DML语句期间,触发体中的SQL语句不能读取或修改触发表的一个约束表的PRIMARY,UNIQUE或FOREIGN关键字的列,然而,如果需求,它们可以修改其他列.
但我试过了,是可以修改PRIMARY列的.为什么?
--------------------------------------------------------------------------------
作者:chenmolin 时间:05-01-27 16:38
真的可以吗?作出来看看。
--------------------------------------------------------------------------------
作者:sbt709394 时间:05-01-27 17:02
SBT表结构如下: SID NUMBER,NAME VARCHAR2(7) PRIMARY KEY,AGE NUMBER
数据如下:
1 AA 1
2 BB 2
3 CC 3
4 D 4
WT表结构如下:NAME VARCHAR2(7) FROEIGN KEY REFERENCES SBT(NAME)
数据如下:
AA
触发器如下:
CREATE OR REPLACE TRIGGER TR BEFORE UPDATE ON WT FOR EACH ROW
BEGIN
UPDATE SBT SET NAME='A' WHERE AGE=1;
END;
/
最后结果:
16:47:10 SQL> UPDATE WT SET NAME='D' WHERE NAME='AA';
已更新 1 行。
16:47:40 SQL> SELECT * FROM SBT;
SID NAME AGE
---------- ------- ----------
1 A 1
2 BB 2
3 CC 3
4 D 4
16:47:46 SQL> SELECT * FROM WT;
NAME
-------
D
--------------------------------------------------------------------------------
作者:sbt709394 时间:05-01-28 17:56
楼上大哥,快来看看................
--------------------------------------------------------------------------------
作者:wnj 时间:05-01-29 08:59
会不会你修改的那个表不满足约束表的条件呢?
--------------------------------------------------------------------------------
作者:wnj 时间:05-01-29 16:54
我找到原因了,也是书上的:
注意,如果只INSERT一行,那么该行的行前和行后触发器就不把触发表看做变异表,这也是行级触发器能过读取或修改触发表的唯一情况。
如INSERT INTO TABLE SELECT ....语句,触发器仍然会把触发表当做变异表看待,即使插入一行也不可以。
不要忘记,UPDATE相当于先删除后插入的操作,而且书上也说了:“这也是行级触发器能过读取或修改触发表的唯一情况”。
那也就是说对触发表一行记录的DML操作在行级触发器是可行的。
--=======================================================================================================
触发器与变异表
触发器与变异表
变异表:当前正在被DML语句更新的表,对于触发器而言,变异表就是在其上定义该触发器的那张表。
触发器中的sql语句不能:(适用于所有行级触发器)
读取或更新触发语句的任何变异表,包括触发表本身。
读取或更新该处发表的约束表的主键列,惟一性健列或外健列,
变异表示例:
将每一门主修课程的学生人数限制在5人,在student表上定义一个before insert or update行级触发器实现
(a)错误示例
Create or replace trigger LIMITMAJORS
BEFORE INSERT OR UPDATE OF major on students
For each row
Declare
V_maxstudents constant number :=5;
V_currentstudents number;
Begin
Select count(*)
Into V_currentstudents
From students
Where major=:new.major;
If V_currentstudents +1> V_maxstudents then
RAISE_APPLICATION_ERROR(-20000,’too many students in major ’||:new.major);
End if;
End limitmajors;
按照上面的写法,触发器查询了它自己的触发表,而触发表又是变异表,
修改:
将上面的触发器修改成2个触发器,行级与语句级触发器,在行级触发器中,可以记录 :new.major 的值,但不查询students表
查询在语句级触发器中实现。
Create or replace package studentdata as
Type t_majors is table OF students.major%type
Index by BINARY_INTEGER;
Type t_IDs is table OF students.ID%type
Index by BINARY_INTEGER;
V_studentmajors t_majors;
V_studentids t_ids;
V_numentries binary_integer:=0;
End studentdata;
/
Create or replace trigger rlimitmajors
Before insert or update of major on students
For each row
Begin
Studentdata.v_numentries:=studentdata.v_numentries+1;
Studentdata.v_studentmajors(studentdata.v_numentries)
:=:new.major;
Studentdata.v_stundentids(studentdata.v_numentries):=
:new.id;
End rlimitmajors;
/
Create or replace trigger slimitmajors
after insert or update of major on students
declare
v_maxstudents constant number:=2;
v_currentstudents number;
v_studentid students.id%type;
v_major students.major%type;
begin
for v_loopindex in 1..studentdata.v_numentries loop
v_studentid :=studentdata.v_studentids(v_loopindex);
v_major:= studentdata.v_studentmajors(v_loopindex);
select count(*)
into v_currentstudents
from students
where major= v_major;
If V_currentstudents > V_maxstudents then
RAISE_APPLICATION_ERROR(-20000,’too many students in major ’||:new.major);
End if;
End loop;
Studentdata.v_numentries:=0;
End slimitmajors;
说明
1. 确保变量成为全局变量的唯一方法就是将它存放在某个包中。
2. 注意用after语句级触发器中进行处理
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/15734550/viewspace-665385/ ,如需转载,请注明出处,否则将追究法律责任。