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