Как известно, компонента Управление распределенными базами данных (УРБД) работает только с одинаковыми конфигурациями. Но есть способ получать информацию из базы данных, конфигурация которой отличается не только кодом, но и структурой.
Допустим, у нас имеется 2 базы данных:
- База для управленческого либо оперативного сводного учета (в дальнейшем "Главная")
- База, работающая с УРБД, установленная на филиалах, розничных точках и т.п. (в дальнейшем "Розничная")
Требуется передавать некоторую сводную информацию (допустим, о продажах товаров) из розничной базы в главную.
Можно пользоваться каким-то собственным обменом за период с помощью средств прямого доступа к базе SQL, универсальным обменом, настроенным при помощи конвертации данных, собственным обменом файлами и проч. Но такой способ неудобен в связи с тем, что мы не знаем точно, какие объекты и в каком периоде менялись в розничной базе. Хотелось бы, чтобы изменения, производимые задним числом, также были отражены в главной базе.
Можно использовать Менеджер обмена данными, но это также может оказаться не слишком удобным из-за того, что МОД работает с объектами (документы, справочники и проч.), т. е. придется не только устанавливать МОД в розничной базе, но и предусматривать передачу информации именно в тех разрезах, которые необходимы, создавать в главной базе какие-то специальные механизмы приема такой информации и ее обработки, чтобы получить сводную информацию.
Я использовал "нетипичное" применение компоненты УРБД.
Для осуществления нашего замысла потребуется создать новую периферийную розничную базу со статусом "только получатель". Ее задача - собирать измененные объекты из всех распределенных баз.
Если в правилах миграции для объектов указана область распространения "Место создания и центр" и информация об изменении этого объекта нужна в главной базе, то необходимо заполнить служебную базу как получателя информации об изменениях этого объекта:
Нам нужно будет получать информацию об измененных объектах, формировать ответ об успешном принятии пакета и ждать новой порции объектов.
Как только база будет создана, можно ее тут же удалить, записав предварительно ее уникальный идентификатор, он еще понадобится для формирования файла 1Cv77Dld.id. Проще провести один обмен и взять готовый файл в качестве шаблона. Об этом позже.
В главной базе есть сводный документ, которые делает движения по управленческому регистру остатков товаров. В розничной базе тысячи движений в день, нам нужны сводные движения. Первоочередная задача - получить информацию о том, какой день по какому объекту нужно перегрузить, чтобы остатки в главной базе были достоверными.
Весь приведенный ниже код выполняется в главной базе. Служебная периферийная база (розничная) находится на том же сервере что и главная (Server).
Для работы понадобится внешняя компонента 1С++.
После ее загрузки необходимо инициализировать переменную:
RecordSet = СоздатьОбъект("ODBCRecordSet");
Сформируем таблицу документов розничной базы, которые изменяют остатки товаров:
ТЗРозн = СоздатьОбъект("ТаблицаЗначений"); ТЗРозн.НоваяКолонка("ВидДокумента"); ТЗРозн.НоваяКолонка("Склад"); ТЗРозн.НоваяСтрока(); ТЗРозн.ВидДокумента = "Приход"; ТЗРозн.Склад = "Склад"; ТЗРозн.НоваяСтрока(); ТЗРозн.ВидДокумента = "Расход"; ТЗРозн.Склад = "Склад"; ТЗРозн.НоваяСтрока(); ТЗРозн.ВидДокумента = "Перемещение"; ТЗРозн.Склад = "СкладОтправитель"; ТЗРозн.НоваяСтрока(); ТЗРозн.ВидДокумента = "Перемещение"; ТЗРозн.Склад = "СкладПолучатель";
Далее подключимся к розничной базе:
ПутьКИсточнику = "\\Server\1C_Conf\BaseR\"; МетаОЛЕ = СоздатьОбъект("MetaDataWork"); МетаОЛЕ.ПрисоединитьМД(ПутьКИсточнику + "1cv7.md"); БазаРозн = "BaseR";
В главной базе каждому складу розничной базы соответствует элемент справочника Склады. Склады розничной базы синхронизированы со складами главной базы с помощью реквизита Код в розничной базе и реквизита КодСинхронизации в главной базе.
Сформируем запрос, который покажет, по каким складам за какие даты необходимо перезагрузить движения:
ТекстЗапросаНач = " |SELECT DISTINCT Запрос.ДатаДок ДатаДок |, Запрос.Склад Склад |, СкладыГ.ID [СкладГ $Справочник.Склады] |FROM ( |"; ТекстЗапросаРозн = ""; ТЗРозн.ВыбратьСтроки(); Пока ТЗРозн.ПолучитьСтроку() = 1 Цикл ТекДок = "ТекДок" + Строка(ТЗРозн.НомерСтроки); ТекстЗапросаРозн = ТекстЗапросаРозн + " |SELECT DISTINCT CAST(LEFT((Журнал.Date_Time_IDDoc),8) as DateTime) ДатаДок |, Склады.ID Склад |, Склады.CODE КодСклад |FROM _1SUPDTS AS Изменения WITH (NOLOCK) |LEFT JOIN _1SJOURN AS Журнал WITH (NOLOCK) ON Изменения.OBJID = Журнал.IDDOC AND $ВидДокумента." + ТЗРозн.ВидДокумента + " = Журнал.IDDOCDEF |LEFT JOIN $Документ." + ТЗРозн.ВидДокумента + " AS " + ТекДок + " WITH (NOLOCK) ON Журнал.IDDOC = " + ТекДок + ".IDDOC |LEFT JOIN $Справочник.Склады AS Склады WITH (NOLOCK) ON $" + ТекДок + "." + ТЗРозн.Склад + " = Склады.ID |WHERE Изменения.TYPEID = $ВидДокумента." + ТЗРозн.ВидДокумента + " |AND Изменения.DBSIGN = 'S01' |"; Если ТЗРозн.НомерСтроки < ТЗРозн.КоличествоСтрок() Тогда ТекстЗапросаРозн = ТекстЗапросаРозн + " |UNION ALL |"; КонецЕсли; КонецЦикла; ТекстЗапросаКон = " |) AS Запрос |LEFT JOIN $Справочник.Склады AS СкладыГ WITH (NOLOCK) ON Запрос.КодСклад = $СкладыГ.КодСинхронизации |"; ТекстЗапросаРозн = МетаОЛЕ.ОбрМетаСКЛ(ТекстЗапросаРозн); ТекстЗапросаРозн = СтрЗаменить(ТекстЗапросаРозн, "FROM", "FROM " + БазаРозн + ".dbo."); ТекстЗапросаРозн = СтрЗаменить(ТекстЗапросаРозн, "LEFT JOIN", "LEFT JOIN " + БазаРозн + ".dbo."); ТЗПериодов = RecordSet.ВыполнитьИнструкцию(ТекстЗапросаНач + ТекстЗапросаРозн + ТекстЗапросаКон);
Далее перебираем таблицу периодов и формируем сводные документы в главной базе. Запрос делаем одновременно к двум базам SQL. Можно было бы использовать метапарсер для нормального именования полей розничной базы, но я напишу, как это выглядело у меня. Главное - показать технологию и дать наглядный пример. Номенклатура в главной и розничной базах синхронизирована по кодам. В розничной базе у регистра используется быстрая обработка движений, поэтому имеется поле Date_Time_Iddoc.
ТЗПериодов.ВыбратьСтроки(); Пока ТЗПериодов.ПолучитьСтроку() = 1 Цикл ТекстЗапроса = " |SELECT CAST(LEFT((ЖурналРозн.Date_Time_IDDoc),8) as DateTime) ДатаДок |, СкладСпр.ID [Склад $Справочник.Склады] |, НоменклатураСпр.ID [Номенклатура $Справочник.Номенклатура] //другие поля |, SUM(ПартииРегРозн.SP9733 * (1 - ПартииРегРозн.DEBKRED * 2)) Количество |, SUM(ПартииРегРозн.SP9807 * (1 - ПартииРегРозн.DEBKRED * 2)) Сумма |FROM " + БазаРозн + ".dbo.RA9731 AS ПартииРегРозн WITH (NOLOCK) |INNER JOIN " + БазаРозн + ".dbo._1SJOURN AS ЖурналРозн WITH (NOLOCK) ON ЖурналРозн.IDDOC = ПартииРегРозн.IDDOC |LEFT JOIN " + БазаРозн + ".dbo.SC10237 AS СкладыСпрРозн WITH (NOLOCK) ON ПартииРегРозн.SP10240 = СкладыСпрРозн.ID |LEFT JOIN $Справочник.Склады AS СкладыСпр WITH (NOLOCK) ON $Склады.КодСинхронизации = СкладыСпрРозн.CODE |LEFT JOIN " + БазаРозн + ".dbo.SC656 AS НоменклатураСпрРозн WITH (NOLOCK) ON ПартииРегРозн.SP9732 = НоменклатураСпрРозн.ID |LEFT JOIN $Справочник.Номенклатура AS НоменклатураСпр WITH (NOLOCK) ON НоменклатураСпрРозн.CODE = НоменклатураСпр.CODE |WHERE ПартииРегРозн.SP10240 = :ВыбСклад |AND ЖурналРозн.Date_Time_IDDoc BETWEEN :ВыбДата AND :ВыбДата~ |GROUP BY CAST(LEFT((ЖурналРозн.Date_Time_IDDoc),8) as DateTime) |, СкладСпр.ID |, НоменклатураСпр.ID // и другие поля из select |"; RecordSet.УстановитьТекстовыйПараметр("ВыбДата", ТЗПериодов.ДатаДок); RecordSet.УстановитьТекстовыйПараметр("ВыбСклад", ТЗПериодов.Склад); ТЗ = RecordSet.ВыполнитьИнструкцию(ТекстЗапроса); //Ищем либо создаем документ в главной базе и заполняем его табличную часть таблицей значений ТЗ КонецЦикла;
Далее производим автообмен со служебной базой, которой может реально не существовать. Проще всего взять имеющийся файл 1Cv77Dld.id из этой базы и использовать его в качестве шаблона. В приведенном примере код центральной базы "001". Идентификаторы, естественно, также нужно заменить.
Для работы кода необходим какой-нибудь архиватор с возможностью выполнения в режиме командной строки. Я использовал pkzip.
ТЗ = RecordSet.ВыполнитьИнструкцию("select max(dwnldid) from " + БазаРозн + ".dbo._1SDWNLDS (nolock) where dbsign = 'S01' and direct = 'O'"); ТекСессия = _StrToId(Лев(ТЗ.ПолучитьЗначение(1,1),6)); СледСессия = ТекСессия + 1; Текст = СоздатьОбъект("Текст"); Стр = " |{7B4B361F-6BF4-4A1E-BD9E-33B20AC3CB62,""001"",0A775160-3248-4AD7-ABDF-600F57EFE92C,""S01"", 1CF1393D-8663-4FBF-96E6-40F72D125E53,""" + Строка(СледСессия) + "|S01"", |{""Acknowledgements"", |{""" + Строка(ТекСессия) + "|001""}}, |{""Constants""}, |{""References""}, |{""Documents""}, |{""Deleted References""}, |{""Deleted Documents""}}"; Текст.ДобавитьСтроку(Стр); Текст.Записать("\\Server\1C_Conf\BaseR\C01\PC\S019\1Cv77Chs.dat"); Текст = СоздатьОбъект("Текст"); Стр = " |{""Download ID"",7B4B361F-6BF4-4A1E-BD9E-33B20AC3CB62,""S01"",1CF1393D-8663-4FBF-96E6-40F72D125E53 ,""001"",0A775160-3248-4AD7-ABDF-600F57EFE92C,""" + Строка(СледСессия) + "|S01""}"; Текст.ДобавитьСтроку(Стр); Текст.Записать("\\Server\1C_Conf\BaseR\C01\PC\S019\1Cv77Dld.id"); КомандаСистемы("\\Server\1C_Conf\BaseR\C01\PC\pkzip -a \\Server\1C_Conf\BaseR\PC\S019.zip \\Server\1C_Conf\BaseR\C01\PC\S019\*.*");
Файлы УРБД для отправки из служебной базы сформированы и заархивированы. Передаваемый пакет ничем от "родного" не отличается. Центральная база розницы обработает его получение и подготовит новую порцию объектов. Задачу можно считать выполненной.
Другие статьи по УРБД:
Структура таблиц УРБД (УРИБ) 1С 7.7
Инструкция по созданию УРБД (УРИБ) 1С 7.7 и обмену в РБД
Как из распределенной базы сделать обычную
Как из обычной базы сделать распределенную (SQL)?
УРБД 1С 7.7. Как быстро создать новую периферийную базу
Другие статьи по прямым запросам:
Ускоряем регистрацию объектов в МОД (для SQL)
Проверка дублей строк с помощью 1С++
Аналог ON DUPLICATE KEY UPDATE в MS SQL
Примеры решения нестандартных задач на T-SQL в 1С
Как написать прямой запрос в 1С (DBF, 1sqlite)
Запросы в 1С к двум базам одновременно (DBF, OLE DB)
Как написать прямой запрос в 1С (SQL) с помощью 1С++
Онлайн резервирование товаров на складе (online reservation)