我正在尝试在表上为插入创建触发器。这是创建脚本:
CREATE OR REPLACE TRIGGER INTEG_QRY_NEW
AFTER INSERT
ON INTEG_LOG
FOR EACH ROW
DECLARE
VAR2 NUMBER(10);
LOG_TEXT1 VARCHAR2(1000);
RESULT1 VARCHAR2(1000);
QUERY_NUM1 VARCHAR2(1000);
QUERY_TIME1 VARCHAR2(1000);
LOG_INDEX1 NUMBER(10);
BEGIN
SELECT COUNT(*) INTO VAR2 FROM USER_TAB_COLS WHERE (COLUMN_NAME = 'RESULT' OR COLUMN_NAME = 'QUERY_NUM' OR COLUMN_NAME = 'DATE_TIME' ) AND TABLE_NAME = 'INTEG_LOG';
IF VAR2=0 THEN
EXECUTE IMMEDIATE 'ALTER TABLE INTEG_LOG ADD RESULT VARCHAR2(50); ALTER TABLE INTEG_LOG ADD DATE_TIME DATE; ALTER TABLE INTEG_LOG ADD QUERY_NUM VARCHAR2(50);';
END IF;
LOG_TEXT1 := :NEW.LOG_TEXT;
QUERY_TIME1 := :NEW.QUERY_TIME;
LOG_INDEX1 := :NEW.LOG_INDEX;
IF QUERY_TIME1 = '' THEN
RESULT1 := '-1';
QUERY_NUM1 := '';
UPDATE INTEG_LOG
SET RESULT = RESULT1,
DATE_TIME = CURRENT_DATE,
QUERY_NUM = QUERY_NUM1
WHERE LOG_INDEX = LOG_INDEX1;
ELSE
RESULT1 := SUBSTR(LOG_TEXT1,(INSTR(LOG_TEXT1,'Result = ')+9),9);
QUERY_NUM1 := SUBSTR(SUBSTR(LOG_TEXT1,INSTR(LOG_TEXT1,'Q# ')+3,20),1,INSTR(SUBSTR(LOG_TEXT1,INSTR(LOG_TEXT1,'Q# ')+3,20),' '));
UPDATE INTEG_LOG
SET RESULT = RESULT1,
DATE_TIME = CURRENT_DATE,
QUERY_NUM = QUERY_NUM1
WHERE LOG_INDEX = LOG_INDEX1;
END IF;
END;
/
触发器正在处理以下列的常规插入(来自应用程序):
LOG_DATE NUMBER(10),
LOG_TIME NUMBER(10),
LOG_TYPE NUMBER(10),
LOG_TEXT VARCHAR2(2000 BYTE),
LOG_INDEX NUMBER(10),
QUERY_TIME VARCHAR2(8 BYTE)
触发器检查以下列是否存在(如果不存在,则添加它们): RESULT VARCHAR2(50 BYTE), DATE_TIME DATE, QUERY_NUM VARCHAR2(50 BYTE)
触发器确实成功创建,但问题是,当我尝试插入表(INTEG_LOG)时,我收到以下错误:
table string.string是变异的,触发器/函数可能看不到它
原因:触发器(或引用的用户定义的plsql函数) 在这个声明中)试图查看(或修改)一个表 在被解雇它的声明修改的过程中。
动作:重写触发器(或函数),使其无法读取 表
这可能是语法错误?请帮忙。谢谢。
答案 0 :(得分:4)
触发器编译的唯一原因是因为INTEG_LOG已经拥有动态SQL尝试添加的列。如果表没有那些列,则触发器不会添加它们,因为触发器将无效,因为那些UPDATE语句将无法编译。
动态SQL应该避免的一个原因是它将编译错误转变为运行时错误。
但是尝试在构建在该表上的触发器中向表中添加列是一个令人惊讶的坏主意。除了其他任何东西,DDL语句发出隐式提交,我们不能在触发器中提交,因为这会对事务造成严重破坏。 (是的,有pragma autonomous_transaction
,但使用它通常是代码味道。
正确的解决方案是:
当然这与由于那些UPDATE语句而导致 的变异表错误无关。我们不能在承载触发器的表上执行DML(包括选择)。我不确定您尝试实施的业务规则,但您需要采用不同的方式来实现。