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