Логирование изменений в системе. 
	  На страницу 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 - что скажешь? | 
			 
		  | 
	 
	
		  | 
	 
	
		 | 
	 
 
  
	 
	    
	   | 
	
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете голосовать в опросах
  | 
   
 
		 |