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