общие триггеры в оракуле - PullRequest
       21

общие триггеры в оракуле

0 голосов
/ 30 октября 2019

Я пишу общий триггер оракула. Предположим, что есть много основных таблиц, таких как PERSON_INFO, EMPLOYEE_INFO и т. Д., И соответствующие им таблицы аудита, такие как PERSON_INFO_AUDIT, EMPLOYEE_INFO_AUDIT. Структура приведена ниже.

PERSON_INFO имеет столбцы: -

            ------------------------------------------------
            |                 PERSON_INFO                  |
            ------------------------------------------------
            |  PERSON_ID   |   FIRST_NAME     |  LAST_NAME |
            |   (NUMBER)   |   (VARCHAR2)     |  (VARCHAR2)|
            ------------------------------------------------
            |    1         |   Andrew         |  Jack      |
            ------------------------------------------------

PERSON_INFO_AUDIT содержит все столбцы PERSON_INFO вместе с двумя дополнительными столбцамиOPERATIONS и AUDIT_DATE.

Требование заключается в том, что если обновляется какая-либо из основных таблиц или удаляется какая-либо строка из основной таблицы, то старые записи основной таблицы должны быть вставлены в соответствующую таблицу аудита.

и затем я пишу обновление, например: -

ОБНОВЛЕНИЕ PERSON_INFO SET FIRST_NAME = 'John';

затем старые значения для PERSON_INFO должен быть вставлен в PERSON_INFO_AUDIT Таблица, подобная приведенной ниже: -

PERSON_INFO_AUDIT должна теперь содержать: -

-------------------------------------------------------------------------
|                         PERSON_INFO_AUDIT                              |
-------------------------------------------------------------------------
|  PERSON_ID   |   FIRST_NAME     |  LAST_NAME |  AUDIT_DATE | OPERATIONS|
|   (NUMBER)   |   (VARCHAR2)     |  (VARCHAR2)| (TIMESTAMP) |   (CHAR)  |
-------------------------------------------------------------------------
|    1         |   Andrew         |  Jack      |  30-08-2019 |     U     |
-------------------------------------------------------------------------

Здесь Audit_date:сегодняшняя дата и операции указывают, были ли удалены строки в главной таблице (D) или обновлены (U). Чтобы облегчить описанный выше сценарий, я написал следующую триггерную функцию.

CREATE OR replace TRIGGER trig_PERSON_INFO_deleteupdate
    after UPDATE OR DELETE 
    ON PERSON_INFO
    FOR EACH ROW
DECLARE
     base_table_name clob;
     audit_table_name clob;
     base_table_cols_in_string clob;
     audit_table_cols_in_string clob;
     operation char;
     final_query clob;    
BEGIN
     base_table_name:= 'PERSON_INFO';
     audit_table_name := base_table_name || '_AUDIT';

     IF UPDATING THEN 
          operation:= 'U';
     ELSE
          operation:= 'D';
     END IF;

     SELECT LISTAGG(COLUMN_NAME, ',') WITHIN GROUP (ORDER BY column_id)
     INTO base_table_cols_in_string
     FROM ALL_TAB_COLUMNS
     WHERE TABLE_NAME= 'PERSON_INFO';

     audit_table_cols_in_string:= base_table_cols_in_string || ',AUDIT_DATE,OPERATIONS';

     final_query:= 'INSERT INTO ' ||  audit_table_name || '(' || audit_table_cols_in_string || ') VALUES(' || ':OLD.PERSON_ID,:OLD.FIRST_NAME,:OLD.LAST_NAME,' || SYSDATE || ',''' || operation || ''');';

     dbms_output.put_line(final_query); 
     EXECUTE IMMEDIATE final_query;
END;

Сформирован запрос:

INSERT INTO PERSON_INFO_AUDIT(PERSON_ID,FIRST_NAME,LAST_NAME,AUDIT_DATE,OPERATIONS) VALUES(:OLD.PERSON_ID,:OLD.FIRST_NAME,:OLD.LAST_NAME,30-10-19,'U');

Однако, когда я пытаюсь выполнить запрос с помощью EXECUTE IMMEDIATE final_query, тогда я получаю ошибку

Ответы [ 3 ]

1 голос
/ 30 октября 2019

Причина этой ошибки:

  1. В конце динамического запроса есть точка с запятой. Удалите эту точку с запятой.
  2. Старые значения таблицы должны быть записаны как имя переменной / столбца, а не как константа (не должны записывать старые значения в '') при подготовке динамического запроса.
  3. Системная дата должна быть написана правильно.

Ниже приведен исправленный код:

create or replace TRIGGER trig_PERSON_INFO_deleteupdate
after UPDATE OR DELETE 
ON PERSON_INFO
FOR EACH ROW
DECLARE

 base_table_name clob;
 audit_table_name clob;
 base_table_cols_in_string clob;
 audit_table_cols_in_string clob;
 operation char;
 final_query clob;      
BEGIN

 base_table_name:= 'PERSON_INFO';
 audit_table_name := base_table_name || '_AUDIT';

 IF UPDATING THEN 
      operation:= 'U';
 ELSE
      operation:= 'D';
 END IF;

 SELECT LISTAGG(COLUMN_NAME, ',') WITHIN GROUP (ORDER BY column_id)
 INTO base_table_cols_in_string
 FROM ALL_TAB_COLUMNS
 WHERE TABLE_NAME= 'PERSON_INFO';

 audit_table_cols_in_string:= base_table_cols_in_string || ',AUDIT_DATE,OPERATIONS';

 final_query:= 'INSERT INTO ' ||  audit_table_name || '(' || audit_table_cols_in_string 
    || ') VALUES(''' || :OLD.PERSON_ID || ''',''' || :OLD.FIRST_NAME || ''',''' || :OLD.LAST_NAME 
    || ''',date ''' || to_char(SYSDATE,'yyyy-mm-dd)' || ''',''' || operation || ''')';

 dbms_output.put_line(final_query); 
 EXECUTE IMMEDIATE final_query;
END;

Надеюсь, это поможет вам:)

1 голос
/ 30 октября 2019

Редактирование моего решения проблемы компиляции , поскольку другое опубликованное решение лучше соответствует вопросу ОП, но я хочу, чтобы дискурсивный бит остался.


Однако мыдолжен действительно задаться вопросом, является ли создание динамического оператора вставки лучшим решением. Во-первых, вам также нужно сгенерировать проекцию предложения VALUES, иначе нет смысла в динамичности. Если структура таблицы изменяется, вам нужно изменить оба набора столбцов. Кроме того, имя таблицы аудита является фиксированным (поскольку имя таблицы, которой принадлежит триггер, является фиксированным). Так что же каждый раз генерирует оператор INSERT? Сравните это с риском (и накладными расходами) динамического DML.

Вся идея «универсальных триггеров» в Oracle ошибочна. SQL является строго типизированным языком, как и PL / SQL. Они работают против предопределенных структур данных. Триггер принадлежит к таблице и работает с текущей структурой таблицы. Поэтому любое аудиторское решение должно признавать этот факт: работать с зерном СУБД, а не против него.

Лучшим подходом было бы создание DDL для триггера из словаря данных и его выполнение статической инструкции вставки. Да, вам нужно будет заново генерировать код триггера каждый раз, когда вы изменяете структуру таблицы, но, честно говоря, если вы так часто меняете структуры таблиц, это становится бременем, тогда у вас есть большие проблемы в процессе моделирования, которые вам нужно решить,


Последний пункт. Начиная с Oracle 11.2.0.4 не было никакой необходимости писать такие триггеры аудита. У Oracle есть возможность, называемая Flashback Data Archive (ранее помеченная как Total Recall), которая автоматически регистрирует любые таблицы, которые нам нужны. Использование встроенной функциональности всегда предпочтительнее, чем наш собственный код. Так что, если вы используете версию Oracle, которая имеет эту возможность, вы обязательно должны ее использовать. Узнать больше .

0 голосов
/ 31 октября 2019

Я нашел общее решение для моего ответа. Моим требованием было, чтобы их было много базовых таблиц и соответствующих им таблиц аудита. Если какая-либо из базовой таблицы будет обновлена ​​/ удалена, я хочу, чтобы соответствующие записи были вставлены в их таблицу аудита. Для этого я хотел использовать общую функцию, которая выполняет эту работу динамически. Я нашел решение и опубликовал одно: -

CREATE OR REPLACE procedure IR_DEV.audit_trigger(main_table_name varchar2)
AS
    audit_table_name clob;
    main_table_col_list clob;
    trig_struct clob;
BEGIN

audit_table_name := main_table_name || '_AUDIT';

select LISTAGG(COLUMN_NAME,',') WITHIN GROUP(ORDER BY column_id)
into main_table_col_list
from COLS where table_name=upper(main_table_name);

trig_struct:='CREATE or REPLACE TRIGGER trig_'||main_table_name ||'_deleteupdate'||chr(10)
             ||'AFTER UPDATE OR DELETE ON '|| main_table_name||chr(10)  
             ||'FOR EACH ROW'||chr(10)
             ||'DECLARE'||chr(10)
             ||'   opt varchar2(1);'||chr(10)
             ||'BEGIN'||chr(10)
             ||'   IF UPDATING THEN'||chr(10) 
             ||'       opt:=''U'';'||chr(10)
             ||'   ELSE'||chr(10)    
             ||'       opt:=''D'';'||chr(10)
             ||'   END IF;'||chr(10)||chr(10)

             ||'   INSERT INTO ' || audit_table_name || ' ('||main_table_col_list||',audit_date,operations )'||chr(10)
             ||'    VALUES ('||':old.'||REPLACE(main_table_col_list,',',',:old.')||',sysdate,opt);'||chr(10)||chr(10)

             ||'END;';

           dbms_output.put_line(trig_struct); 
           execute immediate trig_struct;        
END;

Любые комментарии / предложения высоко ценятся.

...