При разработке в среде 1С:Предприятие одной из наиболее частых задач является выборка данных с помощью запроса и последующее заполнение полученными результатами табличной части (ТЧ) документа или справочника. Несмотря на кажущуюся простоту, этот процесс скрывает в себе множество подводных камней — от неочевидных ошибок типизации параметров до проблем производительности и нюансов клиент-серверного взаимодействия на управляемых формах.
В этой статье мы подробно разберем, как исправить типичные ошибки при передаче логических параметров, рассмотрим различные способы выгрузки результатов запроса в табличную часть и изучим методы оптимизации исходного запроса для ускорения работы базы данных.
Часто начинающие разработчики сталкиваются с ситуацией, когда синтаксически корректный запрос выполняется без ошибок, но возвращает абсолютно пустой результат. Давайте проанализируем фрагмент кода из исходного вопроса:
Запрос.УстановитьПараметр("ПолностьюВыполнен", "Да");
В данном случае поле ПолностьюВыполнен в метаданных документа имеет тип Булево. Передавая в качестве параметра строковое значение "Да", разработчик совершает критическую ошибку типизации.
Выясним причину, почему это происходит. СУБД (система управления базами данных), на которой работает 1С, хранит булевы значения в виде логического нуля или единицы (или True/False). Когда система пытается сопоставить строковый литерал "Да" с логическим типом данных, СУБД не может произвести неявное преобразование типов. В результате сравнение всегда возвращает ложь (False), и условие фильтрации ГДЕ КомплектМЛ.ПолностьюВыполнен = &ПолностьюВыполнен отсекает абсолютно все записи из выборки.
Правильное решение: Всегда передавайте предопределенные логические значения встроенного языка 1С:
Запрос.УстановитьПараметр("ПолностьюВыполнен", Истина);
// Или, если требуется обратное условие:
// Запрос.УстановитьПараметр("ПолностьюВыполнен", Ложь);
Если нам необходимо полностью перезаписать содержимое табличной части результатами выполненного запроса, самым лаконичным и быстродействующим способом является использование встроенного метода Загрузить(). Рассмотрим по шагам, как это реализовать.
Этот метод берет объект типа ТаблицаЗначений и переносит его строки в коллекцию строк табличной части. Посмотрим на пример кода:
РезультатЗапроса = Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
ТаблицаРезультата = РезультатЗапроса.Выгрузить();
Объект.Товары.Загрузить(ТаблицаРезультата);
КонецЕсли;
Однако при использовании метода Загрузить() необходимо учитывать несколько критически важных нюансов:
Загрузить() полностью очищает табличную часть перед заполнением. Если пользователь ранее вручную ввел какие-то строки в документ, они будут безвозвратно удалены.ТрудозатратыПоЗаказу, а в табличной части — Трудозатраты, то данные не будут перенесены, а колонка останется пустой без вызова каких-либо системных ошибок.Что делать, если имена колонок в запросе и в табличной части не совпадают, либо нам требуется не очищать существующие строки ТЧ, а дописывать новые данные в конец? В таком случае нам поможет классический обход выборки или таблицы значений в цикле с использованием функции ЗаполнитьЗначенияСвойств().
Проанализируем ситуацию на примере:
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
// Добавляем новую строку в табличную часть документа
НоваяСтрока = Объект.Товары.Добавить();
// Автоматически заполняем совпадающие реквизиты
ЗаполнитьЗначенияСвойств(НоваяСтрока, Выборка);
// Реквизиты с несовпадающими именами заполняем вручную
НоваяСтрока.Трудозатраты = Выборка.ТрудозатратыПоЗаказу;
КонецЦикла;
Этот подход работает медленнее, чем метод Загрузить(), при обработке десятков тысяч строк, но он предоставляет разработчику полный контроль над процессом заполнения каждой отдельной ячейки.
В современных конфигурациях 1С, работающих на управляемом интерфейсе, действует жесткое разделение контекста выполнения на клиент и сервер. Работа с базой данных и объектами запросов на клиенте невозможна.
Поэтому весь код по формированию запроса и получению данных должен находиться в процедуре с директивой &НаСервере или &НаСервереБезКонтекста.
Рассмотрим два сценария работы с таблицами на управляемой форме:
Если таблица на форме связана с реквизитом Объект (например, Объект.Товары), вы можете использовать стандартный метод загрузки прямо на сервере. Изменения автоматически отобразятся на клиенте:
&НаСервере
Процедура ЗаполнитьТабличнуюЧастьНаСервере()
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ ..."; // Ваш запрос
// Прямая загрузка в ТЧ объекта
Объект.Товары.Загрузить(Запрос.Выполнить().Выгрузить());
КонецПроцедуры
Если на форме создана произвольная таблица с типом ТаблицаЗначений, не связанная с объектом базы данных, напрямую применить к ней метод Загрузить() не получится, так как на форме она представлена типом ДанныеФормыКоллекция. Для обмена данными между серверной таблицей значений и коллекцией формы необходимо использовать специальный метод платформы ЗначениеВДанныеФормы():
&НаСервере
Процедура ЗаполнитьРеквизитФормыНаСервере()
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ ...";
ТаблицаРезультата = Запрос.Выполнить().Выгрузить();
// Преобразуем ТаблицуЗначений в тип ДанныеФормыКоллекция
ЗначениеВДанныеФормы(ТаблицаРезультата, ИмяРеквизитаФормыТаблица);
КонецПроцедуры
Давайте проанализируем исходный запрос автора темы. В нем содержится несколько серьезных архитектурных ошибок проектирования базы данных, которые могут приводить к значительным задержкам при увеличении объема обрабатываемых документов.
Проблема 1: Бессмысленное разыменование ссылок (обращение через точку)
В тексте присутствуют конструкции: КомплектМЛ.Заказ.Ссылка и ЗаказПокупателяТовары.КомплектМЛ.Ссылка. Обращение через точку к реквизиту Ссылка от поля, которое уже является ссылкой, заставляет платформу выполнять лишнюю операцию LEFT JOIN к таблице этого объекта в СУБД. Это избыточная нагрузка на дисковую подсистему и процессор сервера.
Проблема 2: Соединение временных таблиц без предварительной индексации
Во втором и третьем пакетах запроса происходит соединение временных таблиц ТЗ1 и ТЗ2. При объеме временных таблиц более нескольких сотен строк отсутствие индексов заставит СУБД выполнять полное сканирование (Table Scan) при поиске соответствий. Это лавинообразно снижает скорость работы запроса.
Ниже приведена оптимизированная версия исходного запроса, в которой устранены эти проблемы и добавлена правильная индексация связующих полей:
Запрос = Новый Запрос;
Запрос.Текст = "
|ВЫБРАТЬ
| КомплектМЛ.Заказ КАК НомерЗаказа, // Убрали лишнее .Ссылка
| ЗаказПокупателя.Трудозатраты КАК ТрудозатратыПоЗаказу,
| КомплектМЛ.Ссылка КАК КомплектМЛ
|ПОМЕСТИТЬ ТЗ1
|ИЗ
| Документ.КомплектМЛ КАК КомплектМЛ
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.Трудозатраты.Труды КАК ТрудозатратыТруды
| ПО КомплектМЛ.Заказ = ТрудозатратыТруды.Заказ // Убрали лишнее .Ссылка
| ЛЕВОЕ СОЕДИНЕНИЕ Документ.ЗаказПокупателя КАК ЗаказПокупателя
| ПО КомплектМЛ.Заказ = ЗаказПокупателя.Ссылка
|ГДЕ
| КомплектМЛ.ПолностьюВыполнен = &ПолностьюВыполнен
|
|ИНДЕКСИРОВАТЬ ПО
| КомплектМЛ
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ЗаказПокупателяТовары.КомплектМЛ КАК Комплект, // Убрали лишнее .Ссылка
| ЗаказПокупателяТовары.Трудозатраты КАК Трудозатраты,
| ТЗ1.КомплектМЛ КАК КомплектМЛСсылка
|ПОМЕСТИТЬ ТЗ2
|ИЗ
| ТЗ1 КАК ТЗ1
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.ЗаказПокупателя.Товары КАК ЗаказПокупателяТовары
| ПО ТЗ1.КомплектМЛ = ЗаказПокупателяТовары.КомплектМЛ // Убрали лишнее .Ссылка
|
|ИНДЕКСИРОВАТЬ ПО
| Комплект
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| СУММА(ТЗ2.Трудозатраты) КАК Трудозатраты,
| ТЗ1.НомерЗаказа КАК НомерЗаказа
|ИЗ
| ТЗ2 КАК ТЗ2
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ ТЗ1 КАК ТЗ1
| ПО ТЗ2.Комплект = ТЗ1.КомплектМЛ // Быстрое соединение по индексам
|
|СГРУППИРОВАТЬ ПО
| ТЗ1.НомерЗаказа";
Запрос.УстановитьПараметр("ПолностьюВыполнен", Истина); // Используем правильный тип Булево
Благодаря использованию инструкции ИНДЕКСИРОВАТЬ ПО временные таблицы строятся со специальными индексами по полям связей, что обеспечивает мгновенное выполнение соединения СУБД даже при обработке больших массивов информации.