Объектная модель СхемаЗапроса, появившаяся в платформе 1С:Предприятие 8.3, представляет собой мощный инструмент для динамического формирования текстов запросов. Вместо небезопасной и трудоемкой склейки строк, программисты получили возможность манипулировать запросом как набором объектов. Однако при работе с соединениями таблиц часто возникает путаница, приводящая к ошибкам вроде «Индекс находится за границами массива». Разберем подробно, как правильно работать с этой моделью.
Прежде чем переходить к коду, проанализируем иерархию объектов. СхемаЗапроса состоит из коллекции ПакетЗапросов. Каждый элемент пакета (обычно это ЗапросВыбораСхемыЗапроса) содержит коллекцию Операторы. Именно внутри оператора описываются источники данных (таблицы) и связи между ними.
Рассмотрим типичную ошибку начинающих. Когда мы выполняем метод Источники.Добавить("Справочник.Номенклатура", "Товары"), мы просто добавляем таблицу в секцию ИЗ. Если мы следом добавляем вторую таблицу тем же методом, платформа не создает соединение автоматически. Попытка обратиться к Источник.Соединения[0] сразу после добавления источника вызывает ошибку, так как коллекция соединений еще пуста.
Для того чтобы правильно связать две таблицы (например, справочник и регистр), нам необходимо выполнить строгую последовательность действий. Разберем ее на примере создания левого соединения.
Проанализируем пример кода, который решает задачу добавления ЛЕВОГО СОЕДИНЕНИЯ:
// 1. Создаем схему и получаем оператор
СхемаЗапроса = Новый СхемаЗапроса;
ЗапросВыбора = СхемаЗапроса.ПакетЗапросов[0];
Оператор = ЗапросВыбора.Операторы[0];
// 2. Добавляем основной источник (левая таблица)
ИсточникНоменклатура = Оператор.Источники.Добавить("Справочник.Номенклатура", "Товары");
// 3. Добавляем второй источник (правая таблица)
ИсточникЗакупки = Оператор.Источники.Добавить("РегистрНакопления.Закупки.Обороты", "Закупки");
// 4. Важный момент: создание связи.
// Связь всегда принадлежит одному из источников.
// Обычно рекомендуется настраивать все соединения у первого источника.
НовоеСоединение = ИсточникНоменклатура.Соединения.Добавить(ИсточникЗакупки);
НовоеСоединение.ТипСоединения = ТипСоединенияСхемыЗапроса.ЛевоеВнешнее;
// 5. Устанавливаем условие связи (ПО ...)
НовоеСоединение.Условие = Новый ВыражениеСхемыЗапроса("Товары.Ссылка = Закупки.Номенклатура");
Рассмотрим подробнее, почему возникала ошибка в исходном вопросе. Автор пытался обращаться к ИсточникЗакупки.Соединения[0]. Однако, согласно логике СхемыЗапроса, если вы добавили ИсточникЗакупки в общую коллекцию источников оператора, он считается самостоятельной таблицей, пока вы явно не укажете обратное.
Обратите внимание: Если вы добавите источник и сразу попытаетесь изменить его тип соединения через индекс 0, вы столкнетесь с тем, что массив пуст. Чтобы соединение появилось в тексте запроса (как ЛЕВОЕ СОЕДИНЕНИЕ), нужно использовать метод Добавить() именно у коллекции Соединения одного из источников, передавая туда целевой источник.
Выясним также вопрос о "задвоении" таблиц. Если в коде сначала выполняется Оператор.Источники.Добавить(...), а затем для этого же источника создается связь через Соединения.Добавить(...), важно следить за псевдонимами. В объектной модели каждый вызов Добавить создает новый объект. Если вы хотите соединить уже добавленный в оператор источник, передавайте ссылку на него в метод добавления соединения.
Часто соединения происходят с виртуальными таблицами (Обороты, Срезы). В СхемеЗапроса параметры этих таблиц настраиваются отдельно через объект Источник. Посмотрим, как это реализовать:
// Настройка параметров виртуальной таблицы "Закупки.Обороты"
ПараметрыТаблицы = ИсточникЗакупки.Источник.Параметры;
// Установка Начала периода
ПараметрыТаблицы[0].Выражение = Новый ВыражениеСхемыЗапроса("&НачалоПериода");
// Установка Конца периода
ПараметрыТаблицы[1].Выражение = Новый ВыражениеСхемыЗапроса("&КонецПериода");
// Установка периодичности (например, "Месяц")
ПараметрыТаблицы[2].Выражение = Новый ВыражениеСхемыЗапроса("Месяц");
Каждый параметр в коллекции имеет свой индекс, соответствующий документации к конкретной виртуальной таблице. Использование ВыражениеСхемыЗапроса обязательно, так как это типизированный объект для хранения фрагментов кода запроса.
После настройки всех соединений, мы должны указать, какие именно поля должны попасть в итоговую выборку. Рассмотрим, как это сделать программно:
// Добавление полей в выборку
ЗапросВыбора.ВыбираемыеПоля.Добавить("Товары.Наименование", "НоменклатураНаименование");
ЗапросВыбора.ВыбираемыеПоля.Добавить("Закупки.КоличествоОборот", "Количество");
// Получение текста готового запроса
ТекстЗапроса = СхемаЗапроса.ПолучитьТекстЗапроса();
Функция ПолучитьТекстЗапроса() проанализирует все созданные нами объекты (источники, соединения, условия) и сформирует корректную синтаксическую конструкцию на языке запросов 1С. Этот текст затем можно передать в обычный объект Запрос для выполнения.
Рассмотрим, почему стоит тратить время на изучение этой модели. Во-первых, это типизация. Вы оперируете перечислениями, такими как ТипСоединенияСхемыЗапроса, что исключает опечатки в ключевых словах SQL. Во-вторых, это безопасность. Платформа сама расставит скобки и кавычки там, где это необходимо.
Проанализируем ситуацию, когда у вас уже есть текст запроса, и вы хотите его изменить. СхемаЗапроса позволяет загрузить существующий текст методом УстановитьТекстЗапроса(). Это открывает возможности для создания универсальных механизмов: например, программного добавления отборов в сложные отчеты или автоматической замены ЛЕВОГО СОЕДИНЕНИЯ на ВНУТРЕННЕЕ в зависимости от настроек пользователя.
Важно запомнить: Всегда проверяйте количество элементов в коллекциях перед обращением по индексу. Если вы программно модифицируете чужой запрос, используйте методы поиска, например НайтиПоПсевдониму(), чтобы точно определить нужный источник перед тем, как добавлять к нему новые соединения.