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