Как известно, компонента Управление распределенными базами данных (УРБД) работает только с одинаковыми конфигурациями. Но есть способ получать информацию из базы данных, конфигурация которой отличается не только кодом, но и структурой.

Допустим, у нас имеется 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)