новое событие
Информационный поток
Задания вакансии материалы разработки сообщения форума

Ведение журнала регистрации 1С 8.1 в отдельной базе MSSQL.

  • Добавить свою публикацию
  • для этого требуется регистрация

Если ваша база данных в файловом или SQL варианте занимает несколько десятков гигабайт, то вы сполна ощутили унылость ожидания просмотра журнала регистрации при отборе по какому-либо событию или объекту учета. Ожидание порой может достигать нескольких часов! А пользователи зачастую обращаются с просьбой : «Скажите пожалуйста, кто еще кроме меня изменял этот документ и когда?». Идеальным вариантом было бы, если бы пользователь сам открывал дополнительную печатную форму, в которой отображались все изменения по выбранному документу. И потому время формирования выборки этих данных играет существенную роль. Почему 1С так долго отбирает нужные данные? Чтобы это понять, достаточно открыть любой из файлов журнала регистрации, которые хранится на сервере предприятия. Для отбора нужных данных, необходимо зачастую проанализировать все эти файлы. А это гигабайты информации. Сразу же возникает логичный вопрос: если у нас база в SQL формате, то почему бы не хранить журнал регистрации в отдельной базе, используя для выборки необходимых данных всю мощь SQL сервера? Может быть, в будущих релизах компания 1С сделает это, но пока приходится реализовывать эту возможность самостоятельно. Сразу можно рассказать о результате: после внедрения системы время выборки данных при самых сложных отборах не превышает 1-2 секунд!

Впрочем, даже если ваша 1С-база в файловом варианте - ничто не мешает использовать отдельный SQL-сервер для хранения журнала регистрации. Тем более у компании Microsoft есть бесплатный аналог стандартного SQL-сервера – SQL Express. Он хоть и накладывает некоторые ограничения на использование, но в нашем случае они не играют большой роли.

Вкратце механизм работы системы такой : регламентное задание через определенные интервалы времени запускает процедуру выгрузки журнала регистрации в xml-файл (ВыгрузитьЖурналРегистрации()) за период от даты последней выгрузки до текущего момента. После этого полученные данные парсятся и загружаются в SQL-базу. :

Для первоначальной загрузки журнала регистрации можно воспользоваться обработкой Первоначальная выгрузка журнала регистрации.epf. Ее необходимо просто открыть в той 1С-базе, журнал регистрации которой будет выгружаться. Указать период выгрузки, строку соединения с SQL-базой (она уже должна быть создана на SQL-сервере) и путь к скрипту. Скрипт создает необходимые таблицы и хранимые процедуры. Учтите, что первоначальная загрузка может занять достаточно много времени. Поэтому к выбору начального периода выгрузки надо подойти обдуманно.

После этого необходимо создать регламентное задание, содержащее следующий код:

Процедура ВыгрузкаЖурналаРегистрации() Экспорт
	
	СтрокаСоединения = "ЗДЕСЬ ПРОПИСЫВАЕТСЯ СТРОКА СОЕДНИНЕНИЯ С SQL-БАЗОЙ.(Можно взять из строки соединения обработки 'ПервоначальнаяВыгрузкаЖурналаРегистрации.epf')";
	СоединениеАДО = Новый COMОбъект("ADODB.Connection");
	Попытка
		СоединениеАДО.ConnectionTimeout = 0;
		СоединениеАДО.CommandTimeout = 0;
		СоединениеАДО.Open(СтрокаСоединения);
	Исключение
		Сообщить("Ошибка соединения с базой: "+ ОписаниеОшибки());
		Возврат ;
	КонецПопытки;
	
	
	//Выгружаем журнал регистрации за период во временный xml файл
	СтруктураОтбора = Новый Структура("ДатаНачала,ДатаОкончания",НачПериода,КонПериода);
	ИмяФайла = ПолучитьИмяВременногоФайла("xml");
	ВыгрузитьЖурналРегистрации(ИмяФайла,СтруктураОтбора);	  
	
	//Парсим xml файл в таблицу значений
	ТабЖурнала = Новый ТаблицаЗначений();
	СтруктураКолонок = СтрЗаменить("ДатаВремя,РежимЗаписи,Транзакция,Пользователь,Хост,Приложение,НомерСоединения,Событие,ТипСобытия,Комментарий,КодМетаданных,Данные,ПредставлениеДанных,РабочийСервер,ОсновнойIPПорт,ВспомогательныйIPПорт",",",Символы.ПС);
	АнглийискиеИменаКолонок =  СтрЗаменить("Date,TransactionStatus,TransactionID,UserID,Computer,ApplicationName,Connection,EventName,Level,Comment,MetadataName,Data,DataPresentation,ServerName,Port,SyncPort",",",Символы.ПС);
	СоотвКолонок = Новый Соответствие();					   
	Для К = 1 По СтрЧислоСтрок(СтруктураКолонок) Цикл
		ТабЖурнала.Колонки.Добавить(СтрПолучитьСтроку(СтруктураКолонок,К));
		СоотвКолонок.Вставить(СтрПолучитьСтроку(АнглийискиеИменаКолонок,К),СтрПолучитьСтроку(СтруктураКолонок,К));
	КонецЦикла;	
	ТабЖурнала.Колонки.Добавить("КодТаблицы");
	
	ПотокXML = Новый ЧтениеXML;
	ПотокXML.ОткрытьФайл(ИмяФайла);
	ИмяПоля = Неопределено; 
	Итерация = 0;
	Пока ПотокXML.Прочитать() Цикл
		
		ОбработкаПрерыванияПользователя();
		
		Если ПотокXML.ЛокальноеИмя = "Event" И ПотокXML.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
			СтрТаб = ТабЖурнала.Добавить();
		КонецЕсли;
		Если ПустаяСтрока(ПотокXML.ЛокальноеИмя) Тогда
			Если ЗначениеЗаполнено(ПотокXML.Значение) И ПотокXML.КонтекстПространствИмен.Глубина = 3 Тогда
				ИмяКолонки = СоотвКолонок[ИмяПоля];
				Если ИмяКолонки <> Неопределено Тогда
					СтрТаб[ИмяКолонки] = ПотокXML.Значение;
				КонецЕсли;
			КонецЕсли;
			
		Иначе
			ИмяПоля = ПотокXML.ЛокальноеИмя;
			Если ИмяПоля = "Data"  И ПотокXML.ТипУзла = ТипУзлаXML.НачалоЭлемента И ПотокXML.КоличествоАтрибутов() > 1 Тогда
				СтрТаб.КодТаблицы = ПотокXML.ПолучитьАтрибут(1);
			КонецЕсли;
		КонецЕсли;
	КонецЦикла;
	ПотокXML.Закрыть();
	ОбработанныеМетаданные = Новый Соответствие();
	
	
	//Закачиваем полученную таблицу в SQL базу с помощью двух хранимых процедур _UpdateMetadata и _InsertLogData
	Command = Новый COMОбъект("ADODB.Command");
	Command.CommandTimeout = 0;
	Command.ActiveConnection = СоединениеАДО;  
	Command.CommandType = 1;
	
	Итерация = 0;
	ВсегоСтрокЖурнала = ТабЖурнала.Количество();
	Для Каждого СтрТаб Из ТабЖурнала Цикл
		
		
		//Обновляем метаданные
		ТекОбъектМД = СтрТаб.КодМетаданных;
		Если НЕ ПустаяСтрока(ТекОбъектМД) И ОбработанныеМетаданные[ТекОбъектМД] = Неопределено Тогда  	
			ОбработанныеМетаданные.Вставить(ТекОбъектМД,Истина);
			ОбъектМД = НайтиОбъектМД(ТекОбъектМД);						
			Если Найти(ВРег(ТекОбъектМД),ВРег("Recalculation")) > 0 Тогда
				ПредставлениеМетаданных = "Перерасчет. " + ОбъектМД.Синоним;
			Иначе 
				Если ОбъектМД = Неопределено Тогда 
					Продолжить;			
				КонецЕсли;
				ТекОбектМДПоРусски = Строка(ОбъектМД.ПолноеИмя());
				ПредставлениеМетаданных = СформироватьОписание(Лев(ТекОбектМДПоРусски,Найти(ТекОбектМДПоРусски,".")-1)) + 
						" " + Метаданные.НайтиПоПолномуИмени(ТекОбъектМД).Представление();
			КонецЕсли;
			ПредставлениеСсылки = ПолучитьПредставлениеСсылки(ТекОбъектМД);
			
			Если ПредставлениеСсылки <> "" Тогда 
				СтрокаДляСтрокиВнутр = Лев(ПредставлениеСсылки,42);
				НомерТаблицы = Сред(ПредставлениеСсылки,43,Найти(ПредставлениеСсылки,":")-43);
				ТекстЗапроса = "
					|exec _UpdateMetadata 
					|	@MetaData = '" + ТекОбъектМД + "',
					|	@MetaDataPresentation = '" + ПредставлениеМетаданных + "', 
					|	@TabNum = " + НомерТаблицы + ",
					|	@ValueToStringInternal = '"+ СтрокаДляСтрокиВнутр +"'";
			Иначе
				ТекстЗапроса = "
					|exec _UpdateMetadata 
					|	@MetaData = '" + ТекОбъектМД + "',
					|	@MetaDataPresentation = '" + ПредставлениеМетаданных + "' ";
			КонецЕсли;
			
			Command.CommandText = ТекстЗапроса;
		    Попытка
				Command.Execute();
			Исключение
				Возврат;
			КонецПопытки;
			
		КонецЕсли;	
		
		//Загружаем события жунала
		РежимЗаписи = "N";
		Если нрег(СтрТаб.РежимЗаписи) = "committed" Тогда
			РежимЗаписи = "C";
		ИначеЕсли нрег(СтрТаб.РежимЗаписи) = "RolledBack"	Тогда
			РежимЗаписи = "R";
		КонецЕсли;
		
		Данные = "";
		Если НЕ ПустаяСтрока(СтрТаб.Данные) И НЕ ПустаяСтрока(СтрТаб.КодТаблицы) Тогда
			ВнутрДанные = СтрЗаменить(ПолучитьПредставлениеСсылки(СтрТаб.КодТаблицы,СтрТаб.Данные),",",Символы.ПС);
			Данные = СтрПолучитьСтроку(ВнутрДанные,3);
		КонецЕсли;
		
		ТекстЗапроса = " exec _InsertLogData
			|'"+СтрЗаменить(СтрТаб.ДатаВремя,"T"," ")+"' ,
			|'"+РежимЗаписи+"',
			|'"+СтрТаб.Транзакция+"',
			|'"+СтрТаб.Пользователь+"', 
			|'"+СтрТаб.Хост+"',
			|'"+СтрТаб.Приложение+"',
			|'"+ПредставлениеПриложения(СтрТаб.Приложение)+"',
			|"+СтрТаб.НомерСоединения+",
			|'"+СтрТаб.Событие+"',
			|'"+ПредставлениеСобытияЖурналаРегистрации(СтрТаб.Событие)+"',
			|'"+Лев(СтрТаб.ТипСобытия,1)+"',
			|'"+Сред(СтрТаб.Комментарий,1,500) +"',
			|'"+СтрТаб.КодМетаданных+"',	
			|'"+Данные+"',
			|'"+СтрТаб.ПредставлениеДанных+"',
			|'"+СтрТаб.РабочийСервер+"',
			|"+СтрТаб.ОсновнойIPПорт+",
			|"+СтрТаб.ВспомогательныйIPПорт+"
			|";
			
		Command.CommandText = ТекстЗапроса;
	    Попытка
			Command.Execute();
		Исключение
			Возврат;
		КонецПопытки;
		
	КонецЦикла;		
КонецПроцедуры		 

Следует подробнее остановиться на структуре SQL-базы. Чтобы облегчить обработку выборки, все данные хранятся во внутреннем формате 1С. Получение представления ссылки производится с помощью функции ЗначениеВСтрокуВнутр(). Получение ссылки из представления – с помощью обратной функции ЗначениеИзСтрокиВнутр(). Это обеспечивает необходимую скорость преобразования данных в 1С-предприятии.

Основная таблица журнала – это таблица _MAIN. Состав ее колонок схож с составом колонок файла журнала регистрации. Для уменьшения объема и ускорения поиска большинство колонок – это ссылки на ключевые поля в других таблицах. Этот подход позволяет создать необходимые индексы без боязни чрезмерного их разрастания.

Вторая по значимости таблица - _Metadata - описание метаданных. Записи в ней появляются при первом упоминании в журнале регистрации объекта метаданных. Например, если внесли изменения в справочник «Валюты», а записи, описывающей этот справочник в таблице нет – она создается. Параллельно создается таблица, в которой будут храниться ссылки на этот справочник.

  • ID – ключ записи.
  • _MetaData – Описание объекта метаданных в англо-русской нотации
  • _MetaDataPresentation – Полное описание объекта метаданных
  • _TabNum – внутренний номер таблицы, содержащей ссылку на объект метаданных. Например, для документа «Заказ покупателя» номер таблицы равен 204. Следовательно все ссылки на документ этого типа находятся в таблице _Document204.
  • _ValuToStringIntenal – часть строкового представления для функции ЗначениеИзСтрокиВнутр(). Остальная часть берется из ссылочной таблицы. Например, для документа «Заказ покупателя» из таблицы _Document204.

Остальные таблицы :

  • _APP
  • _EVENTS
  • _HOSTS
  • _TRANSACTIONS
  • _USERS
содержат описания соответствующих колонок из таблицы _Main. Кроме вышеперечисленных в базе автоматически создаются необходимые таблицы для хранения ссылок (_DocumentNN, _ReferenceNN…)

Запись новых данных в базу осуществляется с помощью двух встроенных процедур: _InsertLogData и _UpdateMetadata, пример использования которых можно увидеть во фрагменте кода.

Для дальнейшей полноценной работы с новым журналом регистрации предназначены еще две обработки:

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

Все указанные выше файлы, скрипт инициализации MSSQL базы можно скачать в соответствующем разделе файлов контента

 
0
Читайте также
Будущее программ экономического учета.
Какими будут в будущем программы экономического учета программы?
1С помощь по настройке 1С 8.1
Программисты 1С помогают выбрать программу 1С , которая будет удовлетворять требованиям вашего бизнеса
Настройка сервера 1С
Есть два варианта установки и настройки сервера- клиент-серверное приложение и файл-серверное приложение
Разработки
Журнал регистрации 1С в отдельной SQL-базе
Ведение журнала регистрации в 1С 8.1 в SQL базе
Табло для управляемого приложения, тонкий клиент
Обработка - табло управляемого приложения
Очистка базы от документов УУ версия на SQL
Подготовка (очистка базы) от управленческой информации на SQL
Еще от автора
≡ к списку статей