При работе с внешними базами данных через механизм Внешние источники данных в 1С:Предприятие разработчики часто сталкиваются с критической ошибкой: «Получение данных из нескольких источников невозможно». Эта ситуация возникает в тот момент, когда мы пытаемся в тексте одного запроса обратиться одновременно к таблице внешнего источника и к любому внутреннему объекту 1С (например, к справочнику Номенклатура).
В этой статье мы подробно разберем, почему возникает это ограничение, и рассмотрим проверенные способы его обхода, включая работу с временными таблицами в коде и использование Системы компоновки данных (СКД).
Прежде чем переходить к решениям, проанализируем ситуацию с технической стороны. Платформа 1С выполняет запросы на стороне СУБД (SQL Server, PostgreSQL и др.). Когда запрос содержит только объекты основной базы, 1С транслирует его в один SQL-запрос. Когда вы добавляете таблицу из внешнего источника, платформа должна была бы выполнить распределенный запрос между двумя разными базами данных (возможно, даже находящимися на разных серверах или работающими под управлением разных СУБД). Платформа 1С не поддерживает автоматическую трансляцию таких кросс-базовых соединений в одном запросе. Следовательно, соединение должно быть выполнено в памяти сервера приложений 1С.
Это самый распространенный и надежный метод для использования в программном коде. Суть его заключается в том, чтобы сначала получить данные из внешнего источника, сохранить их в промежуточную структуру (Таблицу Значений), а затем передать эту таблицу во второй запрос к основной базе.
Разберем этот процесс по шагам:
ТаблицаЗначений.Номенклатура.Посмотрим на пример реализации в коде:
// Шаг 1: Получаем данные из внешнего источника
ЗапросВнешний = Новый Запрос;
ЗапросВнешний.Текст =
"ВЫБРАТЬ
| ВнешняяТаблица.Код КАК ВнешнийКод,
| ВнешняяТаблица.Цена КАК Цена
|ИЗ
| ВнешнийИсточникДанных.МояБаза.Таблицы.ВнешниеТовары КАК ВнешняяТаблица";
ТаблицаВнешнихДанных = ЗапросВнешний.Выполнить().Выгрузить();
// Шаг 2: Соединяем с номенклатурой в основном запросе
ЗапросОсновной = Новый Запрос;
ЗапросОсновной.УстановитьПараметр("ВнешниеДанные", ТаблицаВнешнихДанных);
ЗапросОсновной.Текст =
"ВЫБРАТЬ
| ВнешниеДанные.ВнешнийКод КАК ВнешнийКод,
| ВнешниеДанные.Цена КАК Цена
|ПОМЕСТИТЬ ВТ_ВнешниеДанные
|ИЗ
| &ВнешниеДанные КАК ВнешниеДанные
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ВТ_ВнешниеДанные.Цена КАК Цена,
| Номенклатура.Ссылка КАК Товар
|ИЗ
| ВТ_ВнешниеДанные КАК ВТ_ВнешниеДанные
| ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Номенклатура
| ПО ВТ_ВнешниеДанные.ВнешнийКод = Номенклатура.Код";
Результат = ЗапросОсновной.Выполнить().Выбрать();
Важное примечание по производительности: Если внешняя таблица содержит тысячи строк, обязательно используйте индексацию временной таблицы. Для этого в тексте запроса после описания временной таблицы добавьте конструкцию ИНДЕКСИРОВАТЬ ПО. Это значительно ускорит операцию соединения.
|ПОМЕСТИТЬ ВТ_ВнешниеДанные
|ИЗ
| &ВнешниеДанные КАК ВнешниеДанные
|ИНДЕКСИРОВАТЬ ПО
| ВнешнийКод
|;
Если ваша задача — вывести отчет, то СКД является лучшим инструментом для решения проблемы нескольких источников. СКД умеет самостоятельно объединять наборы данных в памяти.
Проанализируем алгоритм настройки СКД:
НаборДанныхВнешний.Номенклатура. Назовем его НаборДанных1С.НаборДанныхВнешний является источником связи, а НаборДанных1С — приемником.ВнешнийКод и Код).Платформа сама выполнит два раздельных запроса к базам и соединит результат. Однако помните, что при очень больших объемах данных (сотни тысяч строк) такая связь в СКД может работать медленнее, чем ручная обработка через временные таблицы с индексами.
Выясним причину, по которой соединение может не сработать даже при правильном коде. Внешние базы данных часто используют типы данных, которые не полностью идентичны типам 1С. Например:
CHAR(10), дополняя его пробелами (например, "001 "). В 1С код будет "001". Соединение по равенству не сработает. Рекомендуется использовать функцию СОКРП (в языке 1С) или LTRIM/RTRIM (на стороне внешнего SQL) перед загрузкой.ВЫРАЗИТЬ в запросе 1С, чтобы привести данные к нужному типу.Рассмотрим более продвинутый вариант управления памятью. Если вам нужно выполнять несколько последовательных запросов к внешним и внутренним данным, удобно использовать МенеджерВременныхТаблиц. Это позволяет один раз поместить внешние данные в "темп" (временное хранилище на сервере 1С) и обращаться к ним многократно в рамках одного сеанса.
МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос1 = Новый Запрос("ВЫБРАТЬ * ПОМЕСТИТЬ ВТ_Данные ИЗ &ТЗ КАК ТЗ");
Запрос1.МенеджерВременныхТаблиц = МенеджерВТ;
Запрос1.УстановитьПараметр("ТЗ", ВнешняяТЗ);
Запрос1.Выполнить();
// Теперь ВТ_Данные доступна в любом другом запросе, использующем этот МенеджерВТ
Запрос2 = Новый Запрос("ВЫБРАТЬ Ссылка ИЗ Справочник.Номенклатура ГДЕ Код В (ВЫБРАТЬ Код ИЗ ВТ_Данные)");
Запрос2.МенеджерВременныхТаблиц = МенеджерВТ;
Результат = Запрос2.Выполнить();
Мы выяснили, что прямое соединение внешнего источника и справочника 1С в одном запросе невозможно из-за архитектурных ограничений платформы. Для решения задачи мы всегда должны разделять процесс на два этапа: извлечение данных во временное хранилище (Таблица Значений или Набор данных СКД) и последующее соединение с объектами метаданных 1С. Соблюдение правил индексации и приведение типов данных — залог быстрого и корректного результата.