В практике разработчика 1С часто возникают задачи, связанные с анализом подчиненности документов. Одной из типичных проблем является поиск объектов, которые дублируются в разных табличных частях или связаны с определенным количеством других документов. В данной статье мы разберем, как правильно составить запрос, чтобы найти заказы, включенные ровно в два маршрутных листа, проанализируем типичные ошибки начинающих и рассмотрим наиболее эффективные методы решения этой задачи.
Рассмотрим ситуацию, с которой столкнулся автор темы. Изначально была попытка выбрать данные из табличной части Заказы документа МаршрутныйЛист. Основная сложность заключалась в том, что при стандартном подходе запрос выводит все пары «Маршрутный лист — Заказ», не ограничивая выборку по количеству вхождений.
Разберем типичную ошибку, которую часто допускают при использовании предложения СГРУППИРОВАТЬ ПО. Если мы группируем результат одновременно и по заказу, и по маршрутному листу, то каждая строка в результате запроса всегда будет уникальной. В этом случае агрегатная функция КОЛИЧЕСТВО всегда вернет единицу, так как внутри группы «Заказ №1 + Маршрутный лист №1» существует только одна запись. Для решения нашей задачи нам необходимо изменить логику группировки.
Самый простой и быстрый способ отобрать нужные данные — это сгруппировать записи только по полю Заказ и применить фильтр к результату выполнения агрегатной функции. Давайте посмотрим на пример такого кода:
ВЫБРАТЬ
МаршрутныйЛистЗаказы.Заказ КАК Заказ,
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ МаршрутныйЛистЗаказы.Ссылка) КАК КоличествоДокументов
ИЗ
Документ.МаршрутныйЛист.Заказы КАК МаршрутныйЛистЗаказы
ГДЕ
МаршрутныйЛистЗаказы.Заказ.Дата МЕЖДУ &НачалоПериода И &КонецПериода
СГРУППИРОВАТЬ ПО
МаршрутныйЛистЗаказы.Заказ
ИМЕЮЩИЕ
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ МаршрутныйЛистЗаказы.Ссылка) = 2
Проанализируем этот запрос подробнее:
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ...). Это критически важно, если один и тот же заказ может быть по ошибке добавлен в один и тот же маршрутный лист дважды. В таком случае обычный счетчик выдаст 2, хотя документа фактически два разных не будет.Заказ. Это позволяет «схлопнуть» все упоминания заказа во всех документах в одну строку для анализа.ИМЕЮЩИЕ (аналог HAVING в SQL) отсекает все заказы, которые встречаются в базе чаще или реже двух раз за указанный период.Если в итоговом отчете нам нужно вывести не только сам Заказ, но и список тех самых маршрутных листов, в которые он входит, использование одного простого запроса станет неудобным. В этом случае мы рекомендуем использовать пакетный запрос с временными таблицами. Это повышает читаемость кода и производительность системы.
Разберем этот метод по шагам:
Шаг 1: Отбираем список заказов-дублей во временную таблицу.
ВЫБРАТЬ
МаршрутныйЛистЗаказы.Заказ КАК Заказ
ПОМЕСТИТЬ ВТ_ЗаказыДубли
ИЗ
Документ.МаршрутныйЛист.Заказы КАК МаршрутныйЛистЗаказы
СГРУППИРОВАТЬ ПО
МаршрутныйЛистЗаказы.Заказ
ИМЕЮЩИЕ
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ МаршрутныйЛистЗаказы.Ссылка) = 2
;
Шаг 2: Соединяем временную таблицу с исходными данными для получения полной информации.
ВЫБРАТЬ
МаршрутныйЛистЗаказы.Ссылка КАК Маршрутник,
МаршрутныйЛистЗаказы.Заказ КАК Заказ
ИЗ
ВТ_ЗаказыДубли КАК ЗаказыДубли
ВНУТРЕННЕЕ СОЕДИНЕНИЕ
Документ.МаршрутныйЛист.Заказы КАК МаршрутныйЛистЗаказы
ПО
ЗаказыДубли.Заказ = МаршрутныйЛистЗаказы.Заказ
АВТОУПОРЯДОЧИВАНИЕ
Такой подход позволяет избежать сложных вложенных конструкций и дает возможность легко добавлять дополнительные поля из документов МаршрутныйЛист или Заказ без нарушения логики подсчета количества.
Рассмотрим еще один интересный способ, который упоминался в обсуждении на форуме — это соединение таблицы самой с собой. Этот метод полезен, когда нужно сравнить значения полей в двух разных вхождениях одного и того же объекта.
ВЫБРАТЬ
Лист1.Заказ КАК Заказ,
Лист1.Ссылка КАК Маршрутник1,
Лист2.Ссылка КАК Маршрутник2
ИЗ
Документ.МаршрутныйЛист.Заказы КАК Лист1
ВНУТРЕННЕЕ СОЕДИНЕНИЕ
Документ.МаршрутныйЛист.Заказы КАК Лист2
ПО
Лист1.Заказ = Лист2.Заказ
И Лист1.Ссылка <> Лист2.Ссылка
Выясним причину, по которой этот метод стоит использовать с осторожностью. Если заказ входит в три документа, такой запрос создаст избыточное количество комбинаций строк. Однако для поиска именно «парных» документов это может быть наглядным решением. Главное условие здесь — Лист1.Ссылка <> Лист2.Ссылка, которое гарантирует, что мы нашли разные документы с одинаковым содержанием.
При работе с большими объемами данных в 1С (например, если документов МаршрутныйЛист за год накапливаются десятки тысяч), следует учитывать несколько аспектов:
Заказ в табличной части должно быть индексируемым. Проверьте это в свойствах реквизита в Конфигураторе. Если индекс отсутствует, поиск и группировка будут выполняться крайне медленно.ГДЕ МаршрутныйЛистЗаказы.Заказ.Дата происходит неявное соединение с таблицей документа Заказ. Если вам нужно просто отфильтровать данные по периоду самого маршрутного листа, лучше использовать МаршрутныйЛистЗаказы.Ссылка.Дата, так как ссылка на владельца табличной части всегда находится в основной таблице документа.МенеджерВременныхТаблиц для объекта Запрос, если используете конструкцию ПОМЕСТИТЬ.Подводя итог, можно сказать, что наиболее стабильным и масштабируемым решением является использование временной таблицы с предварительной группировкой и последующим внутренним соединением. Это позволяет гибко настраивать вывод данных в макет, сохраняя точность бизнес-логики.