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