CftClub.ru
Клуб специалистов ЦФТ-Банк

Логирование изменений в системе.
На страницу 1, 2  След.
 
Ответить на тему    Клуб специалистов ЦФТ-Банк (IBSO) -> Справочник PL/PLUS: Функции, примеры, приёмы
Предыдущая тема :: Следующая тема  
Автор Сообщение
Damir
Участник - экстремал
Неподтвержденный


Вступление в Клуб: 29.03.2013
СообщениеЧт Июл 04, 2013 14:12   Логирование изменений в системе. Ответить с цитатой
Полезность: Нет оценки
Из-за недостатка времени накидаю идеи - возможно, сумбурно получится.
Система логирования изменений в ЦФТ не понравилась - появилась идея сделать свою.
Сразу скажу - у самопала есть как плюсы так и минусы.
Идея такая - для Заданой таблицы (Класса) строим таблицу такой же структуры (полная копия) + 3-4 системных поля (SQL-операция 'I', 'D', 'U'; пользователь, дата+время, obj_id - туда ИД копируем). Имя таблицы можно взять такое же + какой-нить суффикс. Это Хистовая таблица.
У ЦФТ есть аналог ARC-таблички, но она транспонирована в строки.
Заполнение Хист-таблички идет в триггере так же как ARC-таблички ЦФТ.
Damir
Участник - экстремал
Неподтвержденный


Вступление в Клуб: 29.03.2013
СообщениеЧт Июл 04, 2013 14:17   Re: Логирование изменений в системе. Ответить с цитатой
Полезность: Нет оценки
Для генерации триггера я написал скриптец.
В принципе, работает. Но щас бы уже писал его по-другому. Поля таблички брал бы из ЦФТ-шного словаря.
Вот скрипт на PL\SQL.
Имя исходной Таблички - присваивается переменной lv_table_name.
На выходе - тело триггера

Код:

declare
 lv_table_name     varchar2(1000);
 lv_histtable_name varchar2(1000); 
 lv_from_field_list   varchar2(4000);
 lv_to_field_list     varchar2(4000);   
 lv_stamp          varchar2(1000) := '       not ((:NEW.<<FIELD>> is null and :OLD.<<FIELD>> is null) or NVL((:NEW.<<FIELD>> = :OLD.<<FIELD>>),false)) or ';
begin
 lv_table_name := 'Z#SRV_TRANS_DATA';
 lv_histtable_name := lv_table_name || '#H';
 
 dbms_output.put_line(replace('CREATE OR REPLACE TRIGGER USR_Z#<<TABLE>>_H', '<<TABLE>>', lv_table_name));
 dbms_output.put_line('AFTER INSERT OR DELETE OR UPDATE');
 dbms_output.put_line( replace('ON COMP.<<TABLE>> FOR EACH ROW', '<<TABLE>>', lv_table_name));
 
 dbms_output.put_line('BEGIN');
 dbms_output.put_line('  declare');
 dbms_output.put_line('    lv_OPER_TYPE varchar2(1);');
 dbms_output.put_line('  begin');
 
 dbms_output.put_line('  if inserting or updating and (');
 
 for rec in(
   select *   
     from(
       select tc.COLUMN_NAME,
         case
           when tc.COLUMN_NAME = 'ID'  then 'C_OBJ_ID'           
           when tc.COLUMN_NAME = 'COLLECTION_ID'  then tc.COLUMN_NAME             
           --when substr(tc.COLUMN_NAME, 1, 2) = 'C_' then 'C_R#'||substr(tc.COLUMN_NAME, 3, 50)
           when substr(tc.COLUMN_NAME, 1, 2) = 'C_' then tc.COLUMN_NAME
         end as new_col_name
       from all_tab_columns tc
         where tc.OWNER = 'COMP'
         and tc.TABLE_NAME = lv_table_name
       order by COLUMN_ID         
     )   
     where new_col_name is not null
   ) loop
--    dbms_output.put_line(rec.new_col_name); 
   dbms_output.put_line(replace(lv_stamp, '<<FIELD>>', rec.column_name)); -- rec.new_col_name));
   lv_from_field_list := lv_from_field_list || ', ' || ':NEW.' ||rec.column_name;   
   lv_to_field_list   := lv_to_field_list || ', ' ||rec.new_col_name;
 end loop;
 lv_from_field_list := substr(lv_from_field_list, 3, 4000);
 lv_to_field_list   := substr(lv_to_field_list, 3, 4000); 
 dbms_output.put_line('       0 = 1  '); 
 dbms_output.put_line('     )then  ');
--    'insert into'
 dbms_output.put_line('     lv_OPER_TYPE := case when inserting then ''I'' else ''U'' end;');
 dbms_output.put_line('     insert into ' || lv_histtable_name||'(');
 dbms_output.put_line('       ID, OPER_TYPE, OPER_TIME, OPER_USER, '||lv_to_field_list); 
 dbms_output.put_line('     )'); 
 dbms_output.put_line('     values('); 
 dbms_output.put_line('       seq_hist_trig.nextval, lv_OPER_TYPE, sysdate, '''', '||lv_from_field_list);
 dbms_output.put_line('     );'); 
 dbms_output.put_line('  end if;');
 
 lv_from_field_list := replace(lv_from_field_list, ':NEW.', ':OLD.');
 dbms_output.put_line('  if deleting then'); 
 dbms_output.put_line('    lv_OPER_TYPE := ''D''; '); 
 dbms_output.put_line('     insert into ' || lv_histtable_name||'(');
 dbms_output.put_line('       ID, OPER_TYPE, OPER_TIME, OPER_USER, '||lv_to_field_list);   
 dbms_output.put_line('     )'); 
 dbms_output.put_line('     values('); 
 dbms_output.put_line('       seq_hist_trig.nextval, lv_OPER_TYPE, sysdate, '''', '||lv_from_field_list); 
 dbms_output.put_line('     );'); 
 dbms_output.put_line('  end if;');
 dbms_output.put_line('  end;');     
 dbms_output.put_line('END;'); 
end;
 

[/code]
Damir
Участник - экстремал
Неподтвержденный


Вступление в Клуб: 29.03.2013
СообщениеЧт Июл 04, 2013 14:26   Re: Логирование изменений в системе. Ответить с цитатой
Полезность: Нет оценки
Остается написать скриптец, чтобы по исходному Классу генерил Хист-класс.
Хист-класс должен из себя представлять планарную структуру, реквизиты класса должны иметь простые Оракловые типы (ну, то что проецируется просто на Оракловые типы - без всяких ссылок и т.д.).
Нужно это, чтоб Ядро не напрягать - к примеру, при переходе по обратным ссылкам чтобы наша Хистовая таблица не светилась нигде.
вот... на скорую руку...
Критика весьма приветствуется.
Ну вот по поводу объемов сразу скажу - да Хист-таблицы будут пухнуть. Их лучше сразу в отдельный ТэйблСпэйс положить.
И еще seq_id для нее жалко - была мысль завести свой сиквенс и заполнять из отрицательного диапазона.
Т.е. сама Хист-таблица становится объектом ИБСО - это удобно для накатывания изменений на рабочую базу, написания вьюшек.
Random
Эксперт
Резидент CftClub


Вступление в Клуб: 27.06.2011
СообщениеПт Июл 05, 2013 08:54   Re: Логирование изменений в системе. Ответить с цитатой
Полезность: 1
заведи просто табличку, без всяких там платформ.
Код:
create table hist#... as select * from ...;
alter table hist# add column modif_time timestamp


Заполняется она из триггеров, id = id, pk = id + modif_time ну и так далее. пользуются оракловские вьюшки user_tables, user_tab_columns.
И сиквенс для неё не нужен.
И из вьюшек она доступна
Код:
type main is select m(m%rowtype) in hist#...%rowtype;


А что касается переносимости - оформи скрипт как функцию с параметром <имя типа>, return boolean.
Сделай библиотеку с вызовом этой функции в макросе:
Код:

-- #if [TYPE].[OPER].CallScript(::[TYPE_NAME]%class)
-- #endif

Всё. Переносишь эти две операции - у тебя создаются нужные hist-таблицы.
Damir
Участник - экстремал
Неподтвержденный


Вступление в Клуб: 29.03.2013
СообщениеПт Июл 05, 2013 10:37   Re: Логирование изменений в системе. Ответить с цитатой
Полезность: Нет оценки
Random пишет:
заведи просто табличку, без всяких там платформ.
Код:
create table hist#... as select * from ...;
alter table hist# add column modif_time timestamp


Составной PK (id+modif_time) - идея свежая, продумаю на досуге.
Для начального создания по-бырому метод вполне подойдет.
Но....
1) Реквизиты исходного класса (поля) добавляются (могут удаляться даже) - надо поддерживать структуру Хист-таблицы в соответствии + триггер пересоздавать. Желательно это делать не на живой базе, а вместе с променением обновлений основного Класса - отсюда требование к Хист-таблице - быть объектом ИБСО.
2) ну и индексы неплохо было бы создать.

М-да.... А вот почему ЦФТ не пошел по такому пути - не понятно. ЦФТ-шные ARC-таблицы в жизни использовать затруднительно.
maestro
Профи
Неподтвержденный


Вступление в Клуб: 12.10.2010
СообщениеПт Июл 05, 2013 10:41   Re: Логирование изменений в системе. Ответить с цитатой
Полезность: Нет оценки
Damir пишет:

Идея такая - для Заданой таблицы (Класса) строим таблицу такой же структуры (полная копия) + 3-4 системных поля (SQL-операция 'I', 'D', 'U'; пользователь, дата+время, obj_id - туда ИД копируем). ЦФТ.


Как быстрый альтернативный вариант, можно флэшбек настроить.
Конечно, всю историю хранить не получится, но денек-другой хранить вполне можно.
Random
Эксперт
Резидент CftClub


Вступление в Клуб: 27.06.2011
СообщениеПт Июл 05, 2013 14:40   Re: Логирование изменений в системе. Ответить с цитатой
Полезность: 1
Damir пишет:

1) Реквизиты исходного класса (поля) добавляются (могут удаляться даже) - надо поддерживать структуру Хист-таблицы в соответствии + триггер пересоздавать. Желательно это делать не на живой базе, а вместе с променением обновлений основного Класса - отсюда требование к Хист-таблице - быть объектом ИБСО.

Хе-хе!
А вот нифига!
Надо, чтобы исходный класс засветился в операции, тогда при перестроении этого класса операция будет перекомпилирована, сработает макрос, вызовет операцию, которая посмотрит список колонок у таблицы и... вуаля Smile
Поэтому, собственно, я и не написал констант 'AC_FIN', а использовал употребление собственно класса ::[AC_FIN]%class
Damir
Участник - экстремал
Неподтвержденный


Вступление в Клуб: 29.03.2013
СообщениеПн Июл 08, 2013 05:37   Re: Логирование изменений в системе. Ответить с цитатой
Полезность: Нет оценки
Random пишет:
Надо, чтобы исходный класс засветился в операции, тогда при перестроении этого класса операция будет перекомпилирована, сработает макрос, вызовет операцию, которая посмотрит список колонок у таблицы и... вуаля Smile
Поэтому, собственно, я и не написал констант 'AC_FIN', а использовал употребление собственно класса ::[AC_FIN]%class


Я потерял мысль.
А как макрос на этапе компиляции сработает?
Random
Эксперт
Резидент CftClub


Вступление в Клуб: 27.06.2011
СообщениеПн Июл 08, 2013 05:46   Re: Логирование изменений в системе. Ответить с цитатой
Полезность: Нет оценки
Damir пишет:
А как макрос на этапе компиляции сработает?

Макрос - выполняется ДО компиляции. В момент разбора текста и формирования PL/PLUS-текста для трансляции его в pl/sql.
Конструкция --#if - это предпосылка к тому, что в зависимости от результата условия в результате может быть получен разный pl/plus-код (и pl/sql, само собой).
В качестве условия стоит вызов функции.
Эта функция по задумке должна выполнять какой-то анализ и возвращать либо true, либо false.
В зависимости от этого, можно выполнять ветвление кода.
Однако внутри функции никто не запрещает обратиться к user_tables, сделать табличку.
Damir
Участник - экстремал
Неподтвержденный


Вступление в Клуб: 29.03.2013
СообщениеПн Июл 08, 2013 06:29   Re: Логирование изменений в системе. Ответить с цитатой
Полезность: Нет оценки
maestro пишет:
Как быстрый альтернативный вариант, можно флэшбек настроить.
Конечно, всю историю хранить не получится, но денек-другой хранить вполне можно.

Вот это вообще не вариант.
1) ограничение по времени хранения
2) ограничение по производительности - сервер редо-логи (или какие там логи) накатывает всякий раз при каждом селекте. Т.е. можно упереться в производительность сервера.
Вопщем - весьма ненадежно.
Damir
Участник - экстремал
Неподтвержденный


Вступление в Клуб: 29.03.2013
СообщениеПн Июл 08, 2013 11:05   Re: Логирование изменений в системе. Ответить с цитатой
Полезность: Нет оценки
Random пишет:

.....
Макрос - выполняется ДО компиляции.
......
Однако внутри функции никто не запрещает обратиться к user_tables, сделать табличку.

Т.е. из Макроса выполняется вызов Функции.
Эта функция должна синхронизировать структкру Хист-Таблицы со структурой ТБП (Класса). А так же перегенерить триггеры.
Я правильно понял?
Триггер надо сгенерить - причем всунуть его ЦФТ-шный словарь.
или нет? или синхронизация структуры Хист-таблицы будет происходить при каждой компиляции (с генерацией триггера на таблицу ЦФТ, без занесения тела триггера в словарь) ?
Random
Эксперт
Резидент CftClub


Вступление в Клуб: 27.06.2011
СообщениеПн Июл 08, 2013 12:47   Re: Логирование изменений в системе. Ответить с цитатой
Полезность: Нет оценки
Damir пишет:

Эта функция должна синхронизировать структкру Хист-Таблицы со структурой ТБП (Класса). А так же перегенерить триггеры.
Я правильно понял?
Триггер надо сгенерить - причем всунуть его ЦФТ-шный словарь.
или нет? или синхронизация структуры Хист-таблицы будет происходить при каждой компиляции (с генерацией триггера на таблицу ЦФТ, без занесения тела триггера в словарь) ?

Ээээ... ты у меня спрашиваешь тех.задание?! Laughing

Хотя... ладно.

Ты поставил задачу - изменяемые данные на схеме нужно копировать в hist-таблицу. Так?
1. На каждой новой схеме ДО установки обновления, решающего эту задачу, hist-таблиц нет.
2. При установке обновления hist-механизма или любого типа из списка "историзируемых", список колонок в hist-таблице должен быть синхронизирован со списком колонок в таблице-источнике.

Так?

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

Для решения этой задачи есть 3 пути.
1. Написать операцию-генератор (ну или скрипт), запустить её один раз, чтобы она сгенерировала кучу справочников для хранения историчных данных, триггеров, передать на боевую схему, а потом огребать кучу несинхронизированных изменённых данных из-за того, что в счета добавили новую колонку. А триггер это не отслеживает, а в справочнике нет квалификатора, чтобы данные этой новой колонки хранить...

2. Написать операцию-генератор (ну или скрипт), установить её на каждую схему, и чтобы она сравнивала таблицу-источник и hist-таблицу. Если изменений нет, ничего делать не надо.
Если удалены колонки в таблице-источнике, нужно переписать триггер.
Если добавлены новые колонки в таблице-источнике, нужно добавить
новые колонки в hist-таблице, и переписать триггер.
Написать инструкцию типа "При установке обновлений, включающих обновления реквизитов в справочниках бла-бла, структурах таких-то и ТБП следующих, сразу после установки необходимо выполнить скрипт такой-то или операцию такую-то".
Стучать по голове администраторам, если они забывают выполнять инструкцию и всё равно огребать несинхронизированные данные.

3. Написать операцию-генератор из п.2. Заметить, что зависимые от справочника операции при установке обновлений этого справочника, перекомпилируются. Использовать это свойство для своих целей (переложить человеческий фактор на машину).

Лично я предложил использовать макросы условной компиляции.

Пробуй, экспериментируй.
Damir
Участник - экстремал
Неподтвержденный


Вступление в Клуб: 29.03.2013
СообщениеВт Июл 09, 2013 05:57   Re: Логирование изменений в системе. Ответить с цитатой
Полезность: Нет оценки
Random пишет:
3. Написать операцию-генератор из п.2. Заметить, что зависимые от справочника операции при установке обновлений этого справочника, перекомпилируются. Использовать это свойство для своих целей (переложить человеческий фактор на машину).

Лично я предложил использовать макросы условной компиляции.

Пробуй, экспериментируй.

При таком подходе Хист-таблица не попадет в словарь ЦФТ.
Для написания вьюшки просмотра Хист-данных придется использовать пл-плюс представление.
Причем джоины (а их будет много) писать руками.
можно, конечно, взять запрос из Представления основного класса и творчески переработать в пл-плюс представление. Но это достаточно нудное и трудоемкое занятие.
Как бы еще автоматизировать написание вьюшки (при условии, что исходный класс имеет вьюшку по умолчанию)?
Random
Эксперт
Резидент CftClub


Вступление в Клуб: 27.06.2011
СообщениеВт Июл 09, 2013 06:12   Re: Логирование изменений в системе. Ответить с цитатой
Полезность: 1
Damir пишет:
При таком подходе Хист-таблица не попадет в словарь ЦФТ.

И не надо. Это даже плюс - можно вынести на отдельные диски, в отдельное табличное пространство, и вообще.
Damir пишет:
... Но это достаточно нудное и трудоемкое занятие.

Всё можно автоматизировать.
Damir пишет:
Как бы еще автоматизировать написание вьюшки (при условии, что исходный класс имеет вьюшку по умолчанию)?

Пример.
Код:

v$str := 'type main in select a(a%rowtype) in ::[DUMMY] all;';
   delete from criteria_tries where criteria_id = v$cid;
   delete from criteria_columns where criteria_id = v$cid;
   v(1) := substr(v$str,1,4000);
   v(2) := substr(v$str,4001,4000);
   v(3) := substr(v$str,8001,4000);
   update criteria
   set    condition = v(1)
      ,   order_by = v(2)
      ,   group_by = v(3)
      ,   properties = '|AllMethods Y|HasClass|NotObjects|PlPlus|'
      ,   not_objects=''
   where id = v$cid;
   data_views.create_vw_crit(v$cid);

Здесь v$str - это переработанный из pl/sql-кода вьющки в pl/plus запрос.
v$cid - идентификатор представления.

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

Правда, есть одно "но". Этот способ не поддерживается. Использование только на свой страх и риск.
Damir
Участник - экстремал
Неподтвержденный


Вступление в Клуб: 29.03.2013
СообщениеПт Июл 12, 2013 10:17   Re: Логирование изменений в системе. Ответить с цитатой
Полезность: Нет оценки
Random пишет:

Всё можно автоматизировать.
Damir пишет:
Как бы еще автоматизировать написание вьюшки (при условии, что исходный класс имеет вьюшку по умолчанию)?


Если:
1) создать обычное Представление у исходного класса...
2) Поправить Оракловую вьюшку - вместо таблицы класса подсунуть Хистовую таблицу.
Вроде, должно получиться....
Random - что скажешь?
Показать сообщения:   
Ответить на тему    Клуб специалистов ЦФТ-Банк (IBSO) -> Справочник PL/PLUS: Функции, примеры, приёмы Часовой пояс: GMT + 3
На страницу 1, 2  След.
Страница 1 из 2

 
Перейти:  
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Рейтинг@Mail.ru