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