Как в запросе 1С выбрать документы за период, рассчитанный в этом же запросе

Программист 1С v8.3 (Управляемые формы) 1С:ERP Управление предприятием Управленческий учет Торговля и дистрибуция
← На главную

При разработке сложных отчетов в системе компоновки данных (СКД) часто возникает задача, когда границы периода не задаются пользователем напрямую, а должны быть вычислены программно внутри самого запроса. Типичный пример: необходимо выбрать документы оприходования, которые были созданы между двумя моментами времени — например, между последним пересчетом товаров за вчерашний день и последним пересчетом за сегодня. В данной статье мы подробно разберем, как реализовать такую логику, используя временные таблицы и эффективные соединения.

Постановка задачи и логика решения

Представим ситуацию: у нас есть базовая дата (параметр &Дата). Нам нужно найти в базе данных два конкретных документа ПересчетТоваров:

  1. Самый последний документ, созданный в день, предшествующий выбранной дате.
  2. Самый последний документ, созданный в саму выбранную дату.

Даты этих двух документов станут границами «Начала» и «Конца» для выборки строк из документов ОприходованиеИзлишковТоваров. Основная сложность заключается в том, чтобы сделать это в рамках одного запроса (одного набора данных СКД), не прибегая к предварительным расчетам во встроенном языке.

Шаг 1: Получение границ периода во временные таблицы

Для начала нам необходимо вычислить начальную и конечную точки отсчета. Мы воспользуемся конструкцией ВЫБРАТЬ ПЕРВЫЕ 1 в сочетании с сортировкой по убыванию даты. Проанализируем, как получить дату последнего пересчета за предыдущий день:


ВЫБРАТЬ ПЕРВЫЕ 1
    ПересчетТоваров.Дата КАК ДатаНачалаОтсчёта
ПОМЕСТИТЬ ВтПересчетНачалоПрихода
ИЗ
    Документ.ПересчетТоваров КАК ПересчетТоваров
ГДЕ
    НАЧАЛОПЕРИОДА(ПересчетТоваров.Дата, ДЕНЬ) = ДОБАВИТЬКДАТЕ(НАЧАЛОПЕРИОДА(&Дата, ДЕНЬ), ДЕНЬ, -1)
    И ПересчетТоваров.Проведен = ИСТИНА
УПОРЯДОЧИТЬ ПО
    ПересчетТоваров.Дата УБЫВ;

Аналогичным образом вычислим дату последнего пересчета за текущий день:


ВЫБРАТЬ ПЕРВЫЕ 1
    ПересчетТоваров.Дата КАК ДатаОкончанияОтсчёта
ПОМЕСТИТЬ ВтПересчетКонецПрихода
ИЗ
    Документ.ПересчетТоваров КАК ПересчетТоваров
ГДЕ
    НАЧАЛОПЕРИОДА(ПересчетТоваров.Дата, ДЕНЬ) = НАЧАЛОПЕРИОДА(&Дата, ДЕНЬ)
    И ПересчетТоваров.Проведен = ИСТИНА
УПОРЯДОЧИТЬ ПО
    ДатаОкончанияОтсчёта УБЫВ;

Важный момент: обратите внимание на условие ПересчетТоваров.Проведен = ИСТИНА. Проверка статуса проведения критически важна, чтобы случайные черновики документов не исказили границы вашего периода.

Шаг 2: Формирование единой таблицы периода

Теперь объединим полученные значения в одну временную таблицу ВтПериод. Поскольку в каждой из исходных таблиц у нас ровно по одной строке (благодаря ПЕРВЫЕ 1), мы можем соединить их по условию ИСТИНА (декартово произведение), что даст нам одну строку с двумя колонками.


ВЫБРАТЬ
    ВтПересчетКонецПрихода.ДатаОкончанияОтсчёта КАК ДатаОкончанияОтсчёта,
    ВтПересчетНачалоПрихода.ДатаНачалаОтсчёта КАК ДатаНачалаОтсчёта
ПОМЕСТИТЬ ВтПериод
ИЗ
    ВтПересчетНачалоПрихода КАК ВтПересчетНачалоПрихода
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВтПересчетКонецПрихода КАК ВтПересчетКонецПрихода ПО (ИСТИНА);

Шаг 3: Основная выборка данных с фильтрацией по периоду

На заключительном этапе мы выбираем данные из табличной части целевого документа. Чтобы использовать вычисленные ранее даты, мы выполним ВНУТРЕННЕЕ СОЕДИНЕНИЕ с нашей таблицей ВтПериод. Условием соединения будет вхождение даты документа в интервал между началом и концом.


ВЫБРАТЬ
    ОприходованиеТовары.Ссылка КАК Ссылка,
    ОприходованиеТовары.Номенклатура КАК Номенклатура,
    ОприходованиеТовары.Количество КАК Количество,
    ОприходованиеТовары.Цена КАК Цена,
    ОприходованиеТовары.Сумма КАК Сумма
ИЗ
    ВтПериод КАК ВтПериод
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.ОприходованиеИзлишковТоваров.Товары КАК ОприходованиеТовары
        ПО ОприходованиеТовары.Ссылка.Дата МЕЖДУ ВтПериод.ДатаНачалаОтсчёта И ВтПериод.ДатаОкончанияОтсчёта
ГДЕ
    ОприходованиеТовары.Ссылка.Проведен = ИСТИНА;

В этом решении мы используем конструкцию ПО ... МЕЖДУ непосредственно в условии соединения. Это позволяет эффективно отсечь лишние записи еще на этапе сопоставления таблиц.

Анализ производительности и оптимизация

Рассмотрим несколько нюансов, которые помогут сделать ваш запрос быстрее и надежнее:

  1. Использование регистров вместо табличных частей: Проанализируем структуру данных. Обращение к табличной части документа Документ.ОприходованиеИзлишковТоваров.Товары — не самый оптимальный путь. Если база данных большая, лучше выбирать данные из соответствующих регистров накопления (например, ТоварыНаСкладах). Это обеспечит автоматическую фильтрацию только по проведенным документам и избавит от лишних соединений с заголовками документов.
  2. Проблема обращения через «точку»: Условие ОприходованиеТовары.Ссылка.Дата заставляет СУБД выполнять неявное соединение табличной части с основной таблицей документа. Для повышения производительности рекомендуется сначала отобрать ссылки на документы во временную таблицу, отфильтровав их по дате в основной физической таблице Документ.ОприходованиеИзлишковТоваров, а затем соединять их с товарами.
  3. Обработка пустых результатов: Выясним причину возможной пустой выборки. Если за вчерашний день не было ни одного документа ПересчетТоваров, таблица ВтПересчетНачалоПрихода будет пустой. В результате ВНУТРЕННЕЕ СОЕДИНЕНИЕ уничтожит все данные, и отчет ничего не выведет. Если такая ситуация возможна, рассмотрите вариант использования ЛЕВОЕ СОЕДИНЕНИЕ и функции ЕСТЬNULL для установки границ по умолчанию.

Альтернативный подход в СКД: использование параметров

Если вы работаете исключительно в СКД, можно пойти иным путем. Вместо сложных конструкций внутри запроса, мы можем определить параметры ДатаНачала и ДатаОкончания на закладке «Параметры» в конструкторе схемы компоновки данных. В поле «Выражение» для этих параметров можно написать вызов функции общего модуля, которая вернет нужные даты, или использовать язык выражений СКД.

Например, в основном запросе вы просто пишете:

ГДЕ Дата МЕЖДУ &ДатаНачала И &ДатаОкончания

А логику поиска последних документов пересчета выносите в отдельную функцию. Это сделает основной запрос гораздо более читаемым и легким в поддержке.

Стандарты оформления кода

Проанализируем технические требования к оформлению кода. При написании запросов и переменных в 1С крайне рекомендуется придерживаться системы стандартов и методик разработки. В частности, стоит избегать использования буквы «ё» в именах полей запроса и переменных (например, вместо ДатаНачалаОтсчёта лучше написать ДатаНачалаОтсчета). Это не является синтаксической ошибкой, но считается хорошим тоном и исключает проблемы при взаимодействии с некоторыми внешними СУБД или при парсинге кода сторонними инструментами.

Таким образом, мы рассмотрели полноценный механизм вычисления периодов «на лету». Использование временных таблиц для хранения границ интервалов — это мощный инструмент, который позволяет реализовывать сложную бизнес-логику в отчетах без написания лишнего кода на языке 1С.

← На главную