Вообще, интеграцию с сервисом ГУВМ МВД по загрузке списка недействительных паспортов мы сделали давно. Примерно также давно, как выпустили первый релиз «АДС: Комплексная проверка контрагентов». Но наш вариант реализации не был лишен некоторых недостатков. В частности, периодически ловили такие проблемы как:
- Иногда, паспорта из этой базы нет-нет да пропадают, но реквизиты пропавших паспортов в БД оседали и, при проверке, все-равно, выбивало, что паспорт недействителен. Представляете себе чувства этого человека? Он, значит, собрал стопку документов, прошелся по всем властным структурам, доказал действительность своего паспорта, а ему при простой операции опять про недействительность говорят…
- Процесс обновления базы был весьма непростым делом. Дело в том, что из-за своего размера база могла обновляться очень (прямо очень) долго, особенно, если у вас нет компьютера с терабайтом оперативки. А обновлять ее надо часто, оптимально – раз в день. И очень хочется, чтобы этот процесс можно было закрыть ночным регламентом.
- Из-за очень большого объема, стандартные инструменты 1С отрабатывали некорректно, что вызывало полную остановку работы сервиса из-за блокировки в момент записи данных в регистр сведений, как средствами 1С, так и средствами самой СУБД MSSQL. Согласитесь, тоже не фонтан.
Посидели, подумали и решили, что нужно с этим что-то делать. Мы пошли путем оптимизации работы загрузчика, в части использования команд на уровне СУБД. Загрузку данных в СУБД выполнить командой BULK INSERT с полной подменой таблицы регистра сведений и с записью в точную копию таблицы на уровне СУБД, с последующим переименованием таблиц. Тем самым выполняем подмену данных без остановки процесса и блокировок. Для решения этой задачи мы бросили умы наших лучших оптимизаторов. И не прогадали!
На выходе мы получили сервис способный обработать большой массив записей, а база недействительных паспортов содержит более 150 млн записей. Сейчас сервис работает без необходимости остановки работы системы, без бесконтрольных блокировок, без хвостов ранее удаленных записей. А также обновление можно запускать каждую ночь с полной уверенностью, что к утру все прогрузится и работа продолжится. Ну и да, сисадмин нашего клиента нам спасибо сказал :)
Теперь давайте немного расскажем про то, как именно, технически, был достигнут необходимый результат. Сразу скажу, информация далее по тексту техническая и требует технических знаний для ее осознания.
Для корректного копирования структуры таблиц на уровне СУБД необходимо определить структуру хранения:
МассивИменМетаданных = Новый Массив();
МассивИменМетаданных.Добавить("РегистрСведений.адс_кпк_Паспорта");
тзСтруктура = ПолучитьСтруктуруХраненияБазыДанных(МассивИменМетаданных,Истина);
ИмяТаблицы1С = СтрЗаменить(тзСтруктура[0].ИмяТаблицы,".","_");
ИмяТаблицыБД = тзСтруктура[0].ИмяТаблицыХранения;
Формируем текст запроса к СУБД, для загрузки данных из файла CSV используем временную таблицу, так же в рамках данного запроса выполняем проверку на наличие копии таблицы данных и создаем точную копию исходной таблицы хранения регистра сведений на уровне СУБД:
ТекстЗапросаСоздатьВременную = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку("
|IF object_id('dbo.%1_tmp', 'U') IS NOT NULL DROP TABLE [dbo].[%1_tmp];
|SELECT * INTO [dbo].[%1_tmp] FROM [dbo].[%1] WHERE 1 = 0;
|IF object_id('dbo.TempPasport', 'U') IS NULL
|CREATE TABLE [dbo].[TempPasport]
|(
| Seria NVARCHAR(4) NOT NULL,
| Number NVARCHAR(6) NOT NULL
|) ELSE TRUNCATE TABLE TempPasport;",
ИмяТаблицыБД));
Формируем текст запроса загрузки данных из файла во временную таблицу командой BULK INSERT:
ТекстЗапросаЗагрузка = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку("
|IF object_id('dbo.TempPasport', 'U') IS NOT NULL
|BULK INSERT [dbo].[TempPasport]
|FROM '%2'
|WITH
|(
|FIRSTROW = 2,
|BATCHSIZE = 100000,
|FIELDTERMINATOR = ',',
|ROWTERMINATOR = '\n',
|ROWS_PER_BATCH = 1700000000,
|CODEPAGE = '1251',
|DATAFILETYPE = 'char'
|);", ИмяТаблицыБД, ИмяФайла);
Формируем текст запроса на копирование данных из временной таблицы в подменную таблицу регистра сведений:
ТекстЗапросаВставка = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку("
|INSERT INTO [dbo].[%1_tmp] SELECT Seria,Number,0 FROM [dbo].[TempPasport];", ИмяТаблицыБД);
Финальный запрос — переименование подменной таблицы:
ТекстЗапросаСкопироватьИзВременной = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку("
|BEGIN TRANSACTION;
|IF object_id('dbo.%1_old', 'U') IS NOT NULL DROP TABLE [dbo].[%1_old];
|EXEC sp_rename '%1', '%1_old';
|EXEC sp_rename '%1_tmp', '%1';
|COMMIT;", ИмяТаблицыБД);
Очистим ненужные данные:
ТекстЗапросаУдалитьВременную = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку("
| DROP TABLE [dbo].[%1_old];
| DROP TABLE [dbo].[TempPasport];", ИмяТаблицыБД);
Собственно, а на этом все. Нехитрый набор действий позволил нам достичь необходимого результата, не ставя лишних костылей.
Хотите узнать больше о нашем продукте? Переходите по ссылке, там еще много всего интересного!
Теги: КПК