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