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