已创建触发器但无法检测到更新
Created triggers but not able to detect the update
您好,我正在尝试创建审核 table,它将在触发器 table 发生变化时插入行 table。
触发:
create or replace TRIGGER ABC
AFTER
UPDATE ON TABLE1
FOR EACH ROW DECLARE
DB_USER VARCHAR2(100);
OS_USER VARCHAR2(100);
IP_ADDRESS VARCHAR2(100);
BEGIN
SELECT USER INTO DB_USER FROM DUAL;
SELECT SYS_CONTEXT('USERENV', 'OS_USER') INTO OS_USER FROM DUAL;
SELECT SYS_CONTEXT('USERENV','IP_ADDRESS')INTO IP_ADDRESS FROM DUAL;
IF UPDATING('NAME'||
'NOTES' ) THEN
ABC_PROC(:NEW.ID,:OLD.NAME,:NEW.NAME,:OLD.NOTES ,:NEW.NOTES
,DB_USER, OS_USER,IP_ADDRESS);
END IF;
END;
程序:
create or replace PROCEDURE ABC_PROC
(
ID IN NUMBER,
OLD_NAME IN VARCHAR2,
NEW_NAME IN VARCHAR2,
OLD_NOTES IN VARCHAR2,
NEW_NOTES IN VARCHAR2,
DB_USER IN VARCHAR2,
OS_USER IN VARCHAR2,
IP_ADDRESS IN VARCHAR2
) AS
BEGIN
IF ( OLD_NAME!= NEW_NAME ) or
( OLD_NOTES != NEW_NOTES )
THEN
INSERT INTO "AUDIT_TABLE"(
ID,
OLD_NAME ,NEW_NAME ,
OLD_NOTES ,NEW_NOTES ,
DBUSER,OSUSER,IP_ADDRESS)
VALUES
(
ID,
OLD_NAME ,NEW_NAME ,
OLD_NOTES ,NEW_NOTES,
DB_USER, OS_USER,IP_ADDRESS
);
END IF;
Exception
when VALUE_ERROR then
DBMS_OUTPUT.PUT_LINE('VALUE ERROR');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('OTHERS SQLCODE:'||SQLCODE||', SQLERRM:'||SQLERRM);
END ABC_PROC;
AUDIT_TABLE:
CREATE TABLE "XCHANGE"."AUDIT_TABLE"
( "ID" NUMBER(19,0) NOT NULL ENABLE,
"OLD_NAME" VARCHAR2(100 BYTE),
"NEW_NAME" VARCHAR2(100 BYTE),
"OLD_NOTES" VARCHAR2(100 BYTE),
"NEW_NOTES" VARCHAR2(100 BYTE),
"DBUSER" VARCHAR2(100 BYTE),
"OSUSER" VARCHAR2(100 BYTE),
"IP_ADDRESS" VARCHAR2(100 BYTE)
) SEGMENT CREATION IMMEDIATE
PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255
NOCOMPRESS LOGGING
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
TABLESPACE "USERS" ;
审核 table、触发器和过程都已成功创建,但是当我更新 table 时,它无法插入更改。
您的触发器包括:
IF UPDATING('NAME'|| 'NOTES') THEN
在 Oracle(和大多数 SQL)中,||
是连接运算符,而不是逻辑的 OR
。因此,只有在更新名为 NAMENOTES
的列时才会发生插入,而不是在更新名为 NAME
或 的名为 [=17] 的列时发生=],正如你想的那样。
改为:
IF UPDATING('NAME') OR UPDATING('NOTES') THEN
顺便说一句,局部变量似乎没有多大意义,您可以直接将 USER
、SYS_CONTEXT('USERENV', 'OS_USER')
等传递到过程调用中。无需从双重选择。
如果您希望审计记录只显示实际更改的值,则需要在值子句中添加一些逻辑,您可以使用 case 表达式来实现。我建议您更改过程参数名称,使它们与列名称不匹配以避免混淆,例如带有 P_
前缀(尽管有些人更喜欢直接使用 table/procedure 名称来标识每个名称的来源):
CREATE OR REPLACE PROCEDURE ABC_PROC (
P_ID IN AUDIT_TABLE.ID%TYPE,
P_OLD_NAME IN AUDIT_TABLE.OLD_NAME%TYPE,
P_NEW_NAME IN AUDIT_TABLE.NEW_NAME%TYPE,
P_OLD_NOTES IN AUDIT_TABLE.OLD_NOTES%TYPE,
P_NEW_NOTES IN AUDIT_TABLE.NEW_NOTES%TYPE,
P_DB_USER IN AUDIT_TABLE.DB_USER%TYPE,
P_OS_USER IN AUDIT_TABLE.OS_USER%TYPE,
P_IP_ADDRESS IN AUDIT_TABLE.IP_ADDRESS%TYPE
) AS
BEGIN
IF (P_OLD_NAME != p_NEW_NAME) or (P_OLD_NOTES != P_NEW_NOTES) THEN
INSERT INTO AUDIT_TABLE (ID, OLD_NAME, NEW_NAME, OLD_NOTES, NEW_NOTES,
DBUSER, OSUSER, IP_ADDRESS)
VALUES (P_ID,
CASE WHEN P_OLD_NAME != P_NEW_NAME THEN P_OLD_NAME END,
CASE WHEN P_OLD_NAME != P_NEW_NAME THEN P_NEW_NAME END,
CASE WHEN P_OLD_NOTES != P_NEW_NOTES THEN P_OLD_NOTES END,
CASE WHEN P_OLD_NOTES != P_NEW_NOTES THEN P_NEW_NOTES END,
P_DB_USER, P_OS_USER, P_IP_ADDRESS);
END IF;
END ABC_PROC;
只有当执行更新的客户端恰好启用了输出时,您的异常处理程序才会显示任何内容,这是您不能依赖的; squashing/hiding 任何错误,尤其是 when others
,真的不是一个好主意。有一天您会发现您没有审计记录,但不知道为什么。
虽然我完全不确定为什么要在此处设置过程,但直接从触发器执行插入会更简单。如果您也计划在其他地方调用过程表单 - 无法想象为什么 - 您可以在过程中获取用户和上下文值,而不是传递它们。您拥有的比较逻辑也不会捕捉到值从 null 变为非 null,反之亦然,但它们可能是主 table 中的非空列。
您好,我正在尝试创建审核 table,它将在触发器 table 发生变化时插入行 table。
触发:
create or replace TRIGGER ABC
AFTER
UPDATE ON TABLE1
FOR EACH ROW DECLARE
DB_USER VARCHAR2(100);
OS_USER VARCHAR2(100);
IP_ADDRESS VARCHAR2(100);
BEGIN
SELECT USER INTO DB_USER FROM DUAL;
SELECT SYS_CONTEXT('USERENV', 'OS_USER') INTO OS_USER FROM DUAL;
SELECT SYS_CONTEXT('USERENV','IP_ADDRESS')INTO IP_ADDRESS FROM DUAL;
IF UPDATING('NAME'||
'NOTES' ) THEN
ABC_PROC(:NEW.ID,:OLD.NAME,:NEW.NAME,:OLD.NOTES ,:NEW.NOTES
,DB_USER, OS_USER,IP_ADDRESS);
END IF;
END;
程序:
create or replace PROCEDURE ABC_PROC
(
ID IN NUMBER,
OLD_NAME IN VARCHAR2,
NEW_NAME IN VARCHAR2,
OLD_NOTES IN VARCHAR2,
NEW_NOTES IN VARCHAR2,
DB_USER IN VARCHAR2,
OS_USER IN VARCHAR2,
IP_ADDRESS IN VARCHAR2
) AS
BEGIN
IF ( OLD_NAME!= NEW_NAME ) or
( OLD_NOTES != NEW_NOTES )
THEN
INSERT INTO "AUDIT_TABLE"(
ID,
OLD_NAME ,NEW_NAME ,
OLD_NOTES ,NEW_NOTES ,
DBUSER,OSUSER,IP_ADDRESS)
VALUES
(
ID,
OLD_NAME ,NEW_NAME ,
OLD_NOTES ,NEW_NOTES,
DB_USER, OS_USER,IP_ADDRESS
);
END IF;
Exception
when VALUE_ERROR then
DBMS_OUTPUT.PUT_LINE('VALUE ERROR');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('OTHERS SQLCODE:'||SQLCODE||', SQLERRM:'||SQLERRM);
END ABC_PROC;
AUDIT_TABLE:
CREATE TABLE "XCHANGE"."AUDIT_TABLE"
( "ID" NUMBER(19,0) NOT NULL ENABLE,
"OLD_NAME" VARCHAR2(100 BYTE),
"NEW_NAME" VARCHAR2(100 BYTE),
"OLD_NOTES" VARCHAR2(100 BYTE),
"NEW_NOTES" VARCHAR2(100 BYTE),
"DBUSER" VARCHAR2(100 BYTE),
"OSUSER" VARCHAR2(100 BYTE),
"IP_ADDRESS" VARCHAR2(100 BYTE)
) SEGMENT CREATION IMMEDIATE
PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255
NOCOMPRESS LOGGING
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
TABLESPACE "USERS" ;
审核 table、触发器和过程都已成功创建,但是当我更新 table 时,它无法插入更改。
您的触发器包括:
IF UPDATING('NAME'|| 'NOTES') THEN
在 Oracle(和大多数 SQL)中,||
是连接运算符,而不是逻辑的 OR
。因此,只有在更新名为 NAMENOTES
的列时才会发生插入,而不是在更新名为 NAME
或 的名为 [=17] 的列时发生=],正如你想的那样。
改为:
IF UPDATING('NAME') OR UPDATING('NOTES') THEN
顺便说一句,局部变量似乎没有多大意义,您可以直接将 USER
、SYS_CONTEXT('USERENV', 'OS_USER')
等传递到过程调用中。无需从双重选择。
如果您希望审计记录只显示实际更改的值,则需要在值子句中添加一些逻辑,您可以使用 case 表达式来实现。我建议您更改过程参数名称,使它们与列名称不匹配以避免混淆,例如带有 P_
前缀(尽管有些人更喜欢直接使用 table/procedure 名称来标识每个名称的来源):
CREATE OR REPLACE PROCEDURE ABC_PROC (
P_ID IN AUDIT_TABLE.ID%TYPE,
P_OLD_NAME IN AUDIT_TABLE.OLD_NAME%TYPE,
P_NEW_NAME IN AUDIT_TABLE.NEW_NAME%TYPE,
P_OLD_NOTES IN AUDIT_TABLE.OLD_NOTES%TYPE,
P_NEW_NOTES IN AUDIT_TABLE.NEW_NOTES%TYPE,
P_DB_USER IN AUDIT_TABLE.DB_USER%TYPE,
P_OS_USER IN AUDIT_TABLE.OS_USER%TYPE,
P_IP_ADDRESS IN AUDIT_TABLE.IP_ADDRESS%TYPE
) AS
BEGIN
IF (P_OLD_NAME != p_NEW_NAME) or (P_OLD_NOTES != P_NEW_NOTES) THEN
INSERT INTO AUDIT_TABLE (ID, OLD_NAME, NEW_NAME, OLD_NOTES, NEW_NOTES,
DBUSER, OSUSER, IP_ADDRESS)
VALUES (P_ID,
CASE WHEN P_OLD_NAME != P_NEW_NAME THEN P_OLD_NAME END,
CASE WHEN P_OLD_NAME != P_NEW_NAME THEN P_NEW_NAME END,
CASE WHEN P_OLD_NOTES != P_NEW_NOTES THEN P_OLD_NOTES END,
CASE WHEN P_OLD_NOTES != P_NEW_NOTES THEN P_NEW_NOTES END,
P_DB_USER, P_OS_USER, P_IP_ADDRESS);
END IF;
END ABC_PROC;
只有当执行更新的客户端恰好启用了输出时,您的异常处理程序才会显示任何内容,这是您不能依赖的; squashing/hiding 任何错误,尤其是 when others
,真的不是一个好主意。有一天您会发现您没有审计记录,但不知道为什么。
虽然我完全不确定为什么要在此处设置过程,但直接从触发器执行插入会更简单。如果您也计划在其他地方调用过程表单 - 无法想象为什么 - 您可以在过程中获取用户和上下文值,而不是传递它们。您拥有的比较逻辑也不会捕捉到值从 null 变为非 null,反之亦然,但它们可能是主 table 中的非空列。