创建触发器但无法检测更新

时间:2015-09-29 18:37:24

标签: sql oracle triggers procedure

您好我正在尝试创建审计表,该行将在触发器表中更改行时插入行。

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" ;

审核表,触发器和过程都是成功创建的,但是当我更新表时,它无法插入更改。

1 个答案:

答案 0 :(得分:2)

您的触发器包括:

IF UPDATING('NAME'|| 'NOTES') THEN

在Oracle(和大多数SQL)中,||是连接运算符,而不是逻辑OR。因此,只有在更新名为NAMENOTES的列时,才会进行插入,而不是更新名为NAME 的列或名为NOTES的列,正如你想象的那样。

取而代之的是:

IF UPDATING('NAME') OR UPDATING('NOTES') THEN

顺便说一句,拥有局部变量似乎没什么意义,你可以直接将USERSYS_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的值,反之亦然,但无论如何它们在主表中可能都不是空列。