您好我正在尝试创建审计表,该行将在触发器表中更改行时插入行。
TRIGGER:
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;
PROCEDURE:
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" ;
审核表,触发器和过程都是成功创建的,但是当我更新表时,它无法插入更改。
答案 0 :(得分:2)
您的触发器包括:
IF UPDATING('NAME'|| 'NOTES') THEN
在Oracle(和大多数SQL)中,||
是连接运算符,而不是逻辑OR
。因此,只有在更新名为NAMENOTES
的列时,才会进行插入,而不是更新名为NAME
的列或名为NOTES
的列,正如你想象的那样。
取而代之的是:
IF UPDATING('NAME') OR UPDATING('NOTES') THEN
顺便说一句,拥有局部变量似乎没什么意义,你可以直接将USER
,SYS_CONTEXT('USERENV', 'OS_USER')
等传递给过程调用。无需从双重选择。
如果您希望审计记录仅显示实际更改的值,则需要在values子句中使用一些逻辑,您可以使用案例表达式。我建议您更改过程参数名称,使它们与列名称不匹配,以避免混淆,例如使用P_
前缀(尽管有些人更喜欢明确使用表/过程名称来识别每个来自的位置):
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;
如果执行更新的客户端恰好启用了输出,您的异常处理程序将只显示任何内容,您不能依赖它;并且压缩/隐藏任何错误,尤其是when others
,确实不是一个好主意。你会发现有一天你没有审计记录,但不知道为什么。
我不知道为什么你这里有一个程序,直接从触发器直接插入会更简单。如果您打算在其他地方调用过程表单 - 无法想象为什么 - 您可以在过程中获取用户和上下文值而不是传递它们。您所拥有的比较逻辑也不会捕获从null更改为非null的值,反之亦然,但无论如何它们在主表中可能都不是空列。