Логирование изменений в системе.
На страницу 1, 2 След.
|
Предыдущая тема :: Следующая тема |
Автор |
Сообщение |
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 Эксперт
Вступление в Клуб: 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 Эксперт
Вступление в Клуб: 27.06.2011
|
Пт Июл 05, 2013 14:40  Re: Логирование изменений в системе. |
|
Полезность: 1
|
Damir пишет: |
1) Реквизиты исходного класса (поля) добавляются (могут удаляться даже) - надо поддерживать структуру Хист-таблицы в соответствии + триггер пересоздавать. Желательно это делать не на живой базе, а вместе с променением обновлений основного Класса - отсюда требование к Хист-таблице - быть объектом ИБСО.
|
Хе-хе!
А вот нифига!
Надо, чтобы исходный класс засветился в операции, тогда при перестроении этого класса операция будет перекомпилирована, сработает макрос, вызовет операцию, которая посмотрит список колонок у таблицы и... вуаля
Поэтому, собственно, я и не написал констант 'AC_FIN', а использовал употребление собственно класса ::[AC_FIN]%class |
|
 |
Damir Участник - экстремал
Вступление в Клуб: 29.03.2013
|
Пн Июл 08, 2013 05:37  Re: Логирование изменений в системе. |
|
Полезность: Нет оценки
|
Random пишет: | Надо, чтобы исходный класс засветился в операции, тогда при перестроении этого класса операция будет перекомпилирована, сработает макрос, вызовет операцию, которая посмотрит список колонок у таблицы и... вуаля
Поэтому, собственно, я и не написал констант 'AC_FIN', а использовал употребление собственно класса ::[AC_FIN]%class |
Я потерял мысль.
А как макрос на этапе компиляции сработает? |
|
 |
Random Эксперт
Вступление в Клуб: 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 Эксперт
Вступление в Клуб: 27.06.2011
|
Пн Июл 08, 2013 12:47  Re: Логирование изменений в системе. |
|
Полезность: Нет оценки
|
Damir пишет: |
Эта функция должна синхронизировать структкру Хист-Таблицы со структурой ТБП (Класса). А так же перегенерить триггеры.
Я правильно понял?
Триггер надо сгенерить - причем всунуть его ЦФТ-шный словарь.
или нет? или синхронизация структуры Хист-таблицы будет происходить при каждой компиляции (с генерацией триггера на таблицу ЦФТ, без занесения тела триггера в словарь) ? |
Ээээ... ты у меня спрашиваешь тех.задание?!
Хотя... ладно.
Ты поставил задачу - изменяемые данные на схеме нужно копировать в 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 Эксперт
Вступление в Клуб: 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 - что скажешь? |
|
 |
|
|
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете голосовать в опросах
|
|