При разработке интерфейсов на платформе 1С:Предприятие 8.3 часто возникает задача визуального информирования пользователя о составе документа прямо в форме списка. Один из самых частых запросов — вывести колонку, которая показывает, пуста табличная часть (ТЧ) документа или в ней есть данные. На первый взгляд задача кажется простой, но при неправильной реализации она может привести к дублированию строк в списке или значительному замедлению работы системы. В этой статье мы подробно разберем несколько способов решения этой задачи, проанализируем типичные ошибки и выберем наиболее оптимальные подходы.
Рассмотрим типичную ситуацию: разработчик пытается добавить поле в запрос динамического списка, просто обращаясь к табличной части через точку. Например, Документ.ЗаказНаПроизводство.Запасы.НомерСтроки. В этом случае система выполняет внутреннее соединение основной таблицы документа с его табличной частью. Если в документе 10 строк, то в результате запроса ссылка на один и тот же документ повторится 10 раз. В форме списка это проявится как размножение одной и той же строки документа, что является критической ошибкой.
Наша задача — получить ровно одну запись для каждого документа, при этом определив, существует ли хотя бы одна строка в его табличной части. Разберем, как это сделать правильно.
Этот метод считается одним из самых простых и производительных для небольших и средних баз данных. Идея заключается в том, чтобы присоединить таблицу состава документа только по первой строке. Если первая строка существует (НомерСтроки = 1), значит, табличная часть заполнена.
Проанализируем пример реализации такого запроса:
ВЫБРАТЬ
ДокументЗаказ.Ссылка КАК Ссылка,
ДокументЗаказ.Дата КАК Дата,
ДокументЗаказ.Номер КАК Номер,
ВЫБОР
КОГДА ЗаказЗапасы.НомерСтроки ЕСТЬ НЕ NULL
ТОГДА ИСТИНА
ИНАЧЕ ЛОЖЬ
КОНЕЦ КАК ТЧ_Заполнена
ИЗ
Документ.ЗаказНаПроизводство КАК ДокументЗаказ
ЛЕВОЕ СОЕДИНЕНИЕ Документ.ЗаказНаПроизводство.Запасы КАК ЗаказЗапасы
ПО ДокументЗаказ.Ссылка = ЗаказЗапасы.Ссылка
И ЗаказЗапасы.НомерСтроки = 1
Важные нюансы этого метода:
ЛЕВОЕ СОЕДИНЕНИЕ, чтобы документы с пустой ТЧ не исчезли из списка.ЗаказЗапасы.НомерСтроки = 1 гарантирует, что к каждому документу приклеится не более одной строки из состава, что исключает дублирование.НомерСтроки будет содержать NULL, что в блоке ВЫБОР превращается в ЛОЖЬ.Если в вашей конфигурации теоретически возможна ситуация, когда первой строки нет (например, из-за специфических программных манипуляций или некорректных обменов), более надежным будет метод с предварительной группировкой табличной части. Мы создаем подзапрос, который возвращает уникальные ссылки документов, присутствующих в таблице состава.
Разберем структуру такого запроса:
ВЫБРАТЬ
ДокументЗаказ.Ссылка КАК Ссылка,
ЕСТЬNULL(СоставДокумента.ЕстьСтроки, ЛОЖЬ) КАК ТЧ_Заполнена
ИЗ
Документ.ЗаказНаПроизводство КАК ДокументЗаказ
ЛЕВОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ
Запасы.Ссылка КАК Ссылка,
ИСТИНА КАК ЕстьСтроки
ИЗ
Документ.ЗаказНаПроизводство.Запасы КАК Запасы
СГРУППИРОВАТЬ ПО
Запасы.Ссылка) КАК СоставДокумента
ПО ДокументЗаказ.Ссылка = СоставДокумента.Ссылка
В данном случае мы сначала "схлопываем" все строки табличной части до уровня ссылки документа, а затем присоединяем этот результат к основному списку. Этот метод работает стабильно, так как гарантирует отсутствие дублей вне зависимости от номеров строк.
Современные версии платформы 1С и СУБД (такие как MS SQL Server или PostgreSQL) эффективно оптимизируют запросы с оператором В (IN). Мы можем реализовать проверку прямо в поле выбора, не загромождая секцию соединений.
Посмотрим на пример кода:
ВЫБРАТЬ
ДокументЗаказ.Ссылка КАК Ссылка,
ВЫБОР
КОГДА ДокументЗаказ.Ссылка В
(ВЫБРАТЬ
ТЧ.Ссылка
ИЗ
Документ.ЗаказНаПроизводство.Запасы КАК ТЧ)
ТОГДА ИСТИНА
ИНАЧЕ ЛОЖЬ
КОНЕЦ КАК ТЧ_Заполнена
ИЗ
Документ.ЗаказНаПроизводство КАК ДокументЗаказ
Этот подход часто оказывается быстрее левого соединения, так как оптимизатор СУБД может использовать полусоединение (semi-join), которое прекращает поиск в табличной части, как только найдена первая подходящая строка для документа.
Рассмотрим ситуацию, когда документ содержит десятки тысяч строк, а список документов открывают сотни пользователей одновременно. В этом случае любой динамический запрос к табличной части будет создавать нагрузку на сервер базы данных. Профессиональное решение в таких высоконагруженных системах — денормализация.
Суть метода заключается в следующем:
ЗаказНаПроизводство новый реквизит ТЧ_Заполнена с типом Булево.ПередЗаписью пропишем логику заполнения этого реквизита:
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
ТЧ_Заполнена = (Запасы.Количество() > 0);
КонецПроцедуры
Преимущества: Максимальная скорость работы списка, мгновенная сортировка и фильтрация по этому полю. Недостатки: Необходимо обновить существующие данные в базе (прогнать обработкой) и поддерживать актуальность реквизита при программном изменении ТЧ.
Выводить в колонку текст "Да" или "Нет" — не самое изящное решение для современного интерфейса. Пользователю гораздо удобнее считывать информацию через иконки. Выясним, как это настроить:
Булево, а число: ВЫБОР КОГДА ... ТОГДА 1 ИНАЧЕ 0 КОНЕЦ КАК ИндексКартинки.БиблиотекаКартинок.СтатусыДокумента) или свою собственную.ИндексКартинки.Теперь вместо скучного текста в списке будет отображаться галочка или другой символ, если табличная часть заполнена.
Проанализировав все способы, можно сделать следующие выводы:
НомерСтроки = 1).В).Помните, что при редактировании запроса динамического списка важно следить за тем, чтобы свойство Основная таблица было заполнено. Это позволит платформе автоматически строить оптимальные запросы для получения данных порциями (динамическое считывание), что обеспечит плавную прокрутку списка пользователем.