При интеграции нескольких информационных баз на платформе 1С:Предприятие 7.7 разработчики часто сталкиваются с проблемой медленной передачи данных. Традиционное OLE-соединение (COM-соединение) обладает существенными недостатками: оно требует запуска еще одного полновесного экземпляра программы 1С, расходует лицензии, работает крайне медленно на больших объемах данных и подвержено внезапным зависаниям. К счастью, если базы данных работают под управлением СУБД MS SQL Server, мы можем обойти эти ограничения и настроить высокоскоростное прямое взаимодействие на уровне SQL.
В этой статье мы подробно разберем, как реализовать прямые и непрямые SQL-запросы к сторонней базе данных из 1С 7.7. Мы рассмотрим три основных сценария: от простых системных механизмов ADO до мощных возможностей внешней компоненты 1С++ (1cpp.dll), которая умеет автоматически разбирать метаданные сторонних баз.
Если обе базы данных (рабочая и сторонняя) расположены на одном физическом сервере СУБД, нам не обязательно подключать сложные внешние библиотеки. Мы можем обратиться к таблицам соседней базы напрямую через язык T-SQL, используя полное имя таблицы СУБД.
В MS SQL Server к таблицам любой базы на текущем сервере можно обратиться по следующему шаблону:
[Имя_Сторонней_Базы].[Схема_Данных].[Имя_Таблицы]
Схемой данных по умолчанию в базах 1С практически всегда является dbo. Разберем по шагам, как написать такой запрос. Главная сложность здесь заключается в том, что физические имена таблиц (например, справочников или документов) в разных базах генерируются платформой динамически. Если конфигурации абсолютно идентичны, то и имена таблиц совпадут. В таком случае мы можем использовать метапарсер нашей текущей базы для генерации запроса к сторонней базе.
Проанализируем пример кода на языке 1С 7.7 с использованием стандартного класса ODBCRecordset из компоненты 1С++:
// Создаем объект для выполнения прямого запроса
Запрос = СоздатьОбъект("ODBCRecordset");
// Формируем текст запроса к сторонней базе "Trade_DB"
// Предполагаем, что конфигурации баз абсолютно одинаковы
ТекстЗапроса = "
|SELECT
| ExtContr.ID as [Ссылка],
| ExtContr.CODE as [Код],
| ExtContr.DESCR as [Наименование]
|FROM
| Trade_DB.dbo.$Справочник.Контрагенты AS ExtContr (NOLOCK)
|WHERE
| ExtContr.ISFOLDER = 2
|";
// Выполняем запрос и выгружаем результат в Таблицу Значений
ТЗ = СоздатьОбъект("ТаблицаЗначений");
Если Запрос.Открыть(ТекстЗапроса, ТЗ) = 1 Тогда
ТЗ.ВыбратьСтроки();
Пока ТЗ.ПолучитьСтроку() = 1 Цикл
Сообщить("Контрагент из сторонней базы: " + ТЗ.Наименование);
КонецЦикла;
Иначе
Сообщить("Ошибка выполнения запроса: " + Запрос.ПолучитьОписаниеОшибки());
КонецЕсли;
Важное примечание: Этот способ работает идеально только в том случае, если структуры метаданных синхронны, и парсер 1С++ корректно заменяет конструкцию $Справочник.Контрагенты на реальное физическое имя таблицы (например, SC128), которое совпадает в обеих базах. Если конфигурации различаются, этот подход использовать нельзя, так как парсер подставит имя таблицы из текущей базы, а в сторонней базе под этим именем может находиться совершенно другой объект или таблица вообще будет отсутствовать.
Что делать, если конфигурации баз различаются, или они расположены на разных серверах? Самым надежным и профессиональным решением является использование класса ODBCDatabase из библиотеки 1С++.
Этот класс обладает уникальным методом ПрисоединитьИБ(). Метод принимает на вход путь к каталогу сторонней информационной базы 1С 7.7. Компонента самостоятельно считывает файл метаданных (1Cv7.MD) и структуру таблиц (1Cv7.DDS) удаленной базы. Это позволяет писать SQL-запросы к сторонней базе, используя привычные метаимена (с символом $), даже если конфигурации полностью различаются!
Рассмотрим подробнее по шагам, как настроить такое подключение:
ODBCDatabase для связи со сторонней базой.ПрисоединитьИБ(), передавая путь к каталогу, имя пользователя и пароль SQL.ODBCRecordset.УстБД().Посмотрим на пример практической реализации данного метода:
// Шаг 1: Инициализируем подключение к сторонней базе данных
БД_Сторонняя = СоздатьОбъект("ODBCDatabase");
ПутьККаталогуБазы = "ВыборКаталогаИБ\Database_Folder\"; // Укажите реальный путь к сетевой папке базы
ПользовательSQL = "sa";
ПарольSQL = "MyStrongPassword123";
// Шаг 2: Присоединяем информационную базу для чтения метаданных
Если БД_Сторонняя.ПрисоединитьИБ(ПутьККаталогуБазы, ПользовательSQL, ПарольSQL) = 0 Тогда
Предупреждение("Не удалось прочитать структуру метаданных сторонней базы данных!");
Возврат;
КонецЕсли;
// Шаг 3: Создаем объект для выполнения SQL-запроса
Запрос = СоздатьОбъект("ODBCRecordset");
// Шаг 4: Указываем контекст выполнения запроса (сторонняя база)
Запрос.УстБД(БД_Сторонняя);
// Шаг 5: Формируем текст запроса.
// Теперь парсер переведет метаимена в физические таблицы удаленной базы!
ТекстЗапроса = "
|SELECT
| Спр.ID as [Ссылка],
| Спр.CODE as [Код],
| Спр.DESCR as [Наименование],
| $Спр.ИНН as [ИНН]
|FROM
| $Справочник.Контрагенты AS Спр (NOLOCK)
|WHERE
| Спр.ISFOLDER = 2
|";
ТЗ = СоздатьОбъект("ТаблицаЗначений");
Если Запрос.Открыть(ТекстЗапроса, ТЗ) = 1 Тогда
Сообщить("Успешно получено строк: " + ТЗ.КоличествоСтрок());
ТЗ.ВыбратьСтроки();
Пока ТЗ.ПолучитьСтроку() = 1 Цикл
Сообщить("Контрагент: " + ТЗ.Наименование + " | ИНН: " + ТЗ.ИНН);
КонецЦикла;
Иначе
Сообщить("Ошибка при выполнении запроса: " + Запрос.ПолучитьОписаниеОшибки());
КонецЕсли;
Этот метод невероятно удобен. Он полностью избавляет разработчика от необходимости вручную сопоставлять имена таблиц (SC***) и реквизитов (SP***) в сторонней базе данных. Вся черновая работа по сопоставлению ложится на плечи парсера метаданных 1С++.
Иногда возникают ситуации, когда на рабочих станциях пользователей запрещено регистрировать сторонние динамические библиотеки (DLL), такие как 1cpp.dll, из-за политик безопасности домена. В этом случае мы можем решить задачу силами самой операционной системы Windows, используя технологию ADO.
Объекты ADODB.Connection и ADODB.Recordset встроены в Windows и доступны "из коробки". Главный недостаток этого метода — полное отсутствие парсера метаданных. Нам придется писать запрос на чистом T-SQL, используя только физические имена таблиц и полей сторонней базы (например, SC240 вместо $Справочник.Номенклатура).
Выяснить физические имена таблиц можно, открыв файл 1Cv7.DDS в каталоге сторонней базы обычным текстовым редактором, либо сделав предварительный анализ структуры таблиц в SQL Server Management Studio (SSMS).
Выполним подключение по шагам через ADO:
// Создаем COM-объекты для работы с базой данных
Соединение = СоздатьОбъект("ADODB.Connection");
НаборЗаписей = СоздатьОбъект("ADODB.Recordset");
// Формируем строку подключения к SQL-серверу
СтрокаПодключения = "Provider=SQLOLEDB.1;" +
"Data Source=Имя_Или_IP_Сервера_SQL;" +
"Initial Catalog=Имя_Базы_Данных_SQL;" +
"User ID=sa;" +
"Password=Пароль_Пользователя_SQL;";
Попытка
Соединение.Open(СтрокаПодключения);
Исключение
Предупреждение("Не удалось установить соединение с сервером базы данных: " + ОписаниеОшибки());
Возврат;
КонецПопытки;
// Пишем прямой SQL-запрос с физическими именами таблиц.
// В примере SC142 - это таблица справочника Контрагенты в сторонней БД.
ТекстЗапроса = "
|SELECT
| SC142.CODE,
| SC142.DESCR
|FROM
| SC142 (NOLOCK)
|WHERE
| SC142.ISFOLDER = 2
|";
Попытка
// Выполняем запрос
НаборЗаписей.Open(ТекстЗапроса, Соединение, 1, 1); // adOpenKeyset, adLockReadOnly
Исключение
Сообщить("Ошибка выполнения запроса SQL: " + ОписаниеОшибки());
Соединение.Close();
Возврат;
КонецПопытки;
// Обходим полученный набор данных
Пока НаборЗаписей.EOF = 0 Цикл
Код = СокрЛП(НаборЗаписей.Fields("CODE").Value);
Наименование = СокрЛП(НаборЗаписей.Fields("DESCR").Value);
Сообщить("Получено по ADO -> Код: " + Код + " | Название: " + Наименование);
НаборЗаписей.MoveNext();
КонецЦикла;
// Обязательно закрываем соединения для высвобождения ресурсов сервера
НаборЗаписей.Close();
Соединение.Close();
Этот способ незаменим для простых перегрузок данных, когда состав полей редко меняется, и нет возможности развернуть полноценную среду разработки с интеграцией сторонних DLL на всех клиентских машинах.
Если базы находятся на разных физических серверах, то помимо прямого указания IP-адреса в строке подключения ADO или 1С++, администраторы баз данных часто используют механизм Linked Server (Связанный сервер) на уровне самого MS SQL Server.
Связав сервера один раз на уровне СУБД, мы получаем возможность обращаться к удаленной базе в рамках стандартных запросов первого способа. Синтаксис обращения будет выглядеть так:
[Имя_Удаленного_Сервера].[Имя_Базы].[dbo].[Имя_Таблицы]
Это позволяет объединять данные из локальной и удаленной баз в одном SQL-запросе с помощью операторов JOIN, что обеспечивает феноменальную скорость обработки распределенной информации.
Прямые SQL-запросы позволяют не только считывать информацию (оператор SELECT), но и модифицировать ее напрямую в СУБД. Мы крайне не рекомендуем выполнять операции прямой записи данных в таблицы сторонних баз 1С 7.7.
Запись напрямую в таблицы SQL минует внутреннюю бизнес-логику платформы 1С:
Если вам необходимо передать данные в стороннюю базу с возможностью записи, лучшим решением будет использование транзитных таблиц СУБД. Вы записываете информацию из текущей базы в нейтральную таблицу SQL, а целевая база забирает эти данные и проводит их через свои штатные механизмы (например, через регламентное задание по OLE или XML).