Конференция "Базы" » Аудит триггером составной транзакции
 
  • Гость (03.02.10 10:40) [0]
    Доброго дня!
    Есть программа, которая выполняет в одной транзакции delete, insert, update. Есть триггер:

    create or replace trigger SAMPLE_AUD
     before insert or delete or update on SAMPLE_TABLE
     for each row


    , задача которого отловить все изменения и записать в таблицу аудита. Проблема в том, что отлавливается только insert. Можно ли сделать так, чтобы триггер отрабатывал на каждую команду в транзакции?
  • Sergey13 © (03.02.10 10:49) [1]
    А все действия на одной таблице производятся?
  • Гость (03.02.10 10:50) [2]
    да
  • 12 © (03.02.10 10:51) [3]
    должно работать..
    А что за версия?

    на моей, покоцанной/бесплатной работает и инсерт и апдейт

    > create or replace trigger SAMPLE_AUD
    >  before insert or delete or update on SAMPLE_TABLE
    >  for each row

    BEGIN
    INSERT INTO audit VALUES (:old.name, :new.name)
    END
  • Sergey13 © (03.02.10 10:51) [4]
    > [2] Гость   (03.02.10 10:50)

    А можно сам тригер увидеть?
  • Гость (03.02.10 10:56) [5]
    create or replace trigger INASTRBUD_AUD
     before insert or delete or update on INASTRBUD
     for each row
    declare
     New_Idaud xx_audit.INASTRBUD_AUD.IDAUD%TYPE;
    begin
     SELECT SEQ_INASTRBUD_AUD.NEXTVAL INTO New_Idaud FROM Dual;
     IF Updating THEN
       IF :old.IDNTYPBUDG <> :new.IDNTYPBUDG OR :old.IDNBACC <> :new.IDNBACC OR
          :old.IDNBUDGET <> :new.IDNBUDGET OR
          :old.IDINASTRBUD <> :new.IDINASTRBUD THEN
         INSERT INTO xx_audit.INASTRBUD_AUD
           (IDNTYPBUDG,
            IDNBACC,
            IDNBUDGET,
            IDINASTRBUD,
            IDAUD,
            DML_TYPE,
            DML_TYPEU,
            TERMINAL,
            OS_USER,
            IP_ADRES,
            SESSION_USER)
         VALUES
           (:old.IDNTYPBUDG,
            :old.IDNBACC,
            :old.IDNBUDGET,
            :old.IDINASTRBUD,
            New_Idaud,
            'U',
            '0',
            TRIM(Sys_Context('userenv', 'TERMINAL')),
            TRIM(Sys_Context('userenv', 'OS_USER')),
            TRIM(Sys_Context('userenv', 'IP_ADDRESS')),
            TRIM(Sys_Context('userenv', 'SESSION_USER')));
         INSERT INTO xx_audit.INASTRBUD_AUD
           (IDNTYPBUDG,
            IDNBACC,
            IDNBUDGET,
            IDINASTRBUD,
            IDAUD,
            DML_TYPE,
            DML_TYPEU,
            TERMINAL,
            OS_USER,
            IP_ADRES,
            SESSION_USER)
         VALUES
           (:new.IDNTYPBUDG,
            :new.IDNBACC,
            :new.IDNBUDGET,
            :new.IDINASTRBUD,
            New_Idaud,
            'U',
            '1',
            TRIM(Sys_Context('userenv', 'TERMINAL')),
            TRIM(Sys_Context('userenv', 'OS_USER')),
            TRIM(Sys_Context('userenv', 'IP_ADDRESS')),
            TRIM(Sys_Context('userenv', 'SESSION_USER')));
       END IF;
     ELSIF Inserting THEN
       INSERT INTO xx_audit.INASTRBUD_AUD
         (IDNTYPBUDG,
          IDNBACC,
          IDNBUDGET,
          IDINASTRBUD,
          IDAUD,
          DML_TYPE,
          DML_TYPEU,
          TERMINAL,
          OS_USER,
          IP_ADRES,
          SESSION_USER)
       VALUES
         (:new.IDNTYPBUDG,
          :new.IDNBACC,
          :new.IDNBUDGET,
          :new.IDINASTRBUD,
          New_Idaud,
          'I',
          '',
          TRIM(Sys_Context('userenv', 'TERMINAL')),
          TRIM(Sys_Context('userenv', 'OS_USER')),
          TRIM(Sys_Context('userenv', 'IP_ADDRESS')),
          TRIM(Sys_Context('userenv', 'SESSION_USER')));
     ELSE
       INSERT INTO xx_audit.INASTRBUD_AUD
         (IDNTYPBUDG,
          IDNBACC,
          IDNBUDGET,
          IDINASTRBUD,
          IDAUD,
          DML_TYPE,
          DML_TYPEU,
          TERMINAL,
          OS_USER,
          IP_ADRES,
          SESSION_USER)
       VALUES
         (:old.IDNTYPBUDG,
          :old.IDNBACC,
          :old.IDNBUDGET,
          :old.IDINASTRBUD,
          New_Idaud,
          'D',
          '',
          TRIM(Sys_Context('userenv', 'TERMINAL')),
          TRIM(Sys_Context('userenv', 'OS_USER')),
          TRIM(Sys_Context('userenv', 'IP_ADDRESS')),
          TRIM(Sys_Context('userenv', 'SESSION_USER')));
     END IF;
    end INASTRBUD_AUD;

  • Sergey13 © (03.02.10 11:10) [6]
    > [5] Гость   (03.02.10 10:56)
    на первый взгляд вроде все логично.
    А если сделать три отдельных тригера на каждое действие?
  • Гость (03.02.10 11:12) [7]
    пробовал, отрабатывает один, по последней команде
  • Кщд (03.02.10 13:33) [8]
    триггеры аудита обычно вешают на after
    а конкретно по теме: тест-кейс из sqlplus в студию, пожалуйста
  • Гость (03.02.10 15:24) [9]
    Спасибо, все решилось!

    Дело было в ошибке сравнения с NULL. Надо так:

     IF Updating THEN
       IF nvl(:old.IDNTYPBUDG, 0) <> nvl(:new.IDNTYPBUDG, 0) OR
          nvl(:old.IDNBACC, 0) <> nvl(:new.IDNBACC, 0) OR
          nvl(:old.IDNBUDGET, 0) <> nvl(:new.IDNBUDGET, 0) OR
          nvl(:old.IDINASTRBUD, 0) <> nvl(:new.IDINASTRBUD, 0) THEN

  • Sergey13 © (03.02.10 15:42) [10]
    > [9] Гость   (03.02.10 15:24)

    А как это влияло на НЕсрабатывании на удаление? Или ты не удалял?
  • Ega23 © (03.02.10 15:55) [11]
    А как в Oracle Update работает?
    У меня складывается глубокое убеждение, что в MSSQL он "в кишках" как delete-insert работает.
  • Anatoly Podgoretsky © (03.02.10 16:01) [12]
    > Ega23  (03.02.2010 15:55:11)  [11]

    Update по сути такой и есть, а внутреннея реализация не интересна.
  • Кщд (03.02.10 18:33) [13]
    Гость   (03.02.10 15:24) [9]
    null = 0
    с такой логикой далеко не уедешь
    также, не ясно, зачем при update плодить две записи и почему в логе нет timestamp?
    допустим, двойной вставкой при update страхуетесь от того, что два пользователя одновременно редактируют запись(а Вам, видимо, хочется видеть что менял пользователь и на что, на случай lost update), но как Вы собираетесь определять очередность изменений при таком подходе - совершенно непонятно
    trim, опять же, где нужно и где не нужно

    и принято сообщать, СУБД и версию
    удачи)
  • ANB (04.02.10 12:30) [14]

    > null = 0
    > с такой логикой далеко не уедешь

    Да ничего страшного вообще то


    > также, не ясно, зачем при update плодить две записи

    Вполне нормально. Старое и новое значение.

    > но как Вы собираетесь определять очередность изменений при
    > таком подходе

    SELECT SEQ_INASTRBUD_AUD.NEXTVAL INTO New_Idaud FROM Dual;

    Короче, вполне себе так нормальный триггер. Я бы еще поле для sysdate добавил - не помешает.

    И стандартная ошибка при сравнении (забыли про null). Я так тоже люблю косячить. Правда, в последнее время уже очень редко.
  • Sergey13 © (04.02.10 13:14) [15]
    Насчет аудита я скажу так. Я бы трижды подумал на тему - а нужен ли он такой. Пробовал несколько раз делать такое - очень скоро аудитные таблицы начинают превосходить исходные по объему на порядки. При этом используется он ОЧЕНЬ (обычно) редко и не дает особо ничего. Ну нашли виновника, и что? "Ну ошибся я, с кем не бывает". И ВСЕ. Я бы просто добавил логин юзера и дату последней модификации записи в основную таблицу. В 99% случаев этогшо достаточно.

    Разумеется в некоторых системах аудит необходим даже по закону, но обычно люди пишущие его не задают вопросов на форумах.

    ИМХО

    ЗЫ: Я так и не понял как влияло некорректное сравнение с нулом на секцию тригера про удаление.
  • Sergey13 © (04.02.10 13:20) [16]
    Плюсом про аудит.
    В Оракле (насколько я понял это он) вроде как есть штатное средство по работе со штатными логами. Log Miner вроде называется. Я с ним не разбирался, но вроде как по отзывам вполне работоспособен. А логах оракла есть все.
  • Кщд (04.02.10 17:25) [17]
    >ANB   (04.02.10 12:30) [14]
    >Да ничего страшного вообще то
    0!=null - логическая ошибка
    т.о. при изменении 0 на null и обратно, изменение не будет учтено

    >Вполне нормально. Старое и новое значение.
    вопрос был "зачем"
    если бездумно плодить данные - нормально, то остается только умыть руки

    >SELECT SEQ_INASTRBUD_AUD.NEXTVAL INTO New_Idaud FROM Dual;
    т.е. если ID2 > ID1, то изменения в ID2 - более поздние, так?

    >Короче, вполне себе так нормальный триггер. Я бы еще поле для sysdate добавил - не помешает.
    timestamp
    и, желательно, с local time zone
    впрочем, это уже излишество, но весьма полезное время от времени
  • Кщд (04.02.10 17:29) [18]
    >Sergey13 ©   (04.02.10 13:20) [16]
    logminer пишет IP пользователя?)

    а если серьезно, то, например, на простенькой, некритичной базке с 3-5 таблиц никто не будет поднимать архивлоги - это просто ни к чему

    да и с помощью sql работать куда как приятнее - опять же можно в софтину клиентскую привязать показ изменений
  • Кщд (04.02.10 20:49) [19]
    >Sergey13 ©   (04.02.10 13:14) [15]
    перечитал Ваш пост внимательно)

    >Разумеется в некоторых системах аудит необходим даже по закону, но >обычно люди пишущие его не задают вопросов на форумах.
    ещё как задают))
    здесь ещё немного кода и специфичных названий полей/таблиц - есть примеры, когда выкладывают код в несколько десятков килобайт...
    только когда(и если) работодатель видит потроха базы и кода в форуме - ни к чему хорошему для вопрошавшего это не приводит
 
Конференция "Базы" » Аудит триггером составной транзакции
Есть новые Нет новых   [134434   +28][b:0][p:0.004]