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

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В практике разработки на платформе 1С:Предприятие часто возникает задача: из набора записей, сгруппированных по какому-либо признаку (например, по контрагенту, номенклатуре или документу-основанию), необходимо выбрать только одну строку, которая удовлетворяет определенному критерию «первенства» (самая свежая дата, максимальная сумма или просто первая попавшаяся). Рассмотрим подробно, как решить эту задачу различными методами, проанализируем их производительность и применимость в разных версиях платформы.

Метод 1. Использование коррелированного подзапроса в условии ГДЕ

Этот способ является наиболее гибким, так как позволяет отобрать всю строку целиком, основываясь на сложном порядке сортировки. Суть метода заключается в использовании оператора В в секции ГДЕ, где во вложенном запросе мы выбираем идентификатор (например, Ссылка или КлючЗаписи) первой строки, отфильтрованной по текущей группе из внешнего запроса.

Проанализируем пример кода, который часто используется разработчиками:


ВЫБРАТЬ
    Т1.Поле1 КАК Группа,
    Т1.Поле2 КАК Значение,
    Т1.Поле3 КАК Описание
ИЗ
    ВТ_Данные КАК Т1
ГДЕ
    (Т1.Поле1, Т1.Поле2, Т1.Поле3) В (
        ВЫБРАТЬ ПЕРВЫЕ 1
            Т2.Поле1,
            Т2.Поле2,
            Т2.Поле3
        ИЗ
            ВТ_Данные КАК Т2
        ГДЕ
            Т2.Поле1 = Т1.Поле1
        УПОРЯДОЧИТЬ ПО
            Т2.Приоритет УБЫВ,
            Т2.Дата СУММА
    )

Разберем, как это работает. Для каждой строки основной таблицы Т1 СУБД выполняет вложенный запрос. Во вложенном запросе мы накладываем фильтр ГДЕ Т2.Поле1 = Т1.Поле1, тем самым ограничивая выборку текущей группой. С помощью УПОРЯДОЧИТЬ ПО мы выводим наверх ту запись, которую считаем «первой», а ВЫБРАТЬ ПЕРВЫЕ 1 отсекает всё лишнее.

Важное замечание: такая конструкция может существенно замедлять работу на больших объемах данных, так как является коррелированным подзапросом. СУБД может быть вынуждена выполнять внутренний цикл столько раз, сколько строк находится в таблице Т1.

Метод 2. Использование оконных функций (для платформы 8.3.21 и выше)

Если вы работаете на современной версии платформы 1С (начиная с 8.3.21), то задача решается максимально элегантно и производительно с помощью оконных функций. Это позволяет избежать сложных соединений и коррелированных подзапросов.

Рассмотрим применение функции ПОРЯДКОВЫЙ_НОМЕР():


ВЫБРАТЬ
    ИсходныеДанные.Группа,
    ИсходныеДанные.Значение,
    ПОРЯДКОВЫЙ_НОМЕР() ОБЪЕМЛЮЩИЙ (СЕКЦИЯ ПО ИсходныеДанные.Группа УПОРЯДОЧИТЬ ПО ИсходныеДанные.Дата УБЫВ) КАК НомерСтроки
ПОМЕСТИТЬ ВТ_СНомерами
ИЗ
    Справочник.ВашСправочник КАК ИсходныеДанные
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ_СНомерами.Группа,
    ВТ_СНомерами.Значение
ИЗ
    ВТ_СНомерами КАК ВТ_СНомерами
ГДЕ
    ВТ_СНомерами.НомерСтроки = 1

В данном случае мы создаем временную таблицу, в которой каждая строка внутри своей группы (секции) получает уникальный номер. В следующем шаге мы просто фильтруем результат, оставляя только записи с номером 1. Это самый быстрый способ из доступных в языке запросов 1С.

Метод 3. Классический подход через временные таблицы и группировку

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

Разберем шаги реализации:

  1. Создаем временную таблицу, где выбираем поле группировки и агрегатную функцию для поля, определяющего порядок (например, МАКСИМУМ(Дата)).
  2. Если поле порядка не уникально (например, несколько документов в одну секунду), дополнительно берем МАКСИМУМ(Ссылка).
  3. Соединяем полученную таблицу с исходными данными по всем ключевым полям.

Посмотрим на пример кода:


// Шаг 1: Находим идентификаторы "первых" записей
ВЫБРАТЬ
    Данные.Группа КАК Группа,
    МАКСИМУМ(Данные.МоментВремени) КАК МаксМомент
ПОМЕСТИТЬ ВТ_МаксимальныеДаты
ИЗ
    РегистрНакопления.Продажи КАК Данные
СГРУППИРОВАТЬ ПО
    Данные.Группа
;

////////////////////////////////////////////////////////////////////////////////
// Шаг 2: Получаем полные данные, соединяясь по ключу и моменту времени
ВЫБРАТЬ
    Продажи.Группа,
    Продажи.Номенклатура,
    Продажи.Сумма
ИЗ
    РегистрНакопления.Продажи КАК Продажи
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_МаксимальныеДаты КАК ВТ_Макс
    ПО Продажи.Группа = ВТ_Макс.Группа
    И Продажи.МоментВремени = ВТ_Макс.МаксМомент

Обратите внимание на использование поля МоментВремени. В 1С это поле гарантирует более точную сортировку, чем обычная Дата, так как включает в себя ссылку на объект, что позволяет различать записи внутри одной секунды.

Метод 4. Метод «Приоритетов» через ВЫБОР КОГДА

В ситуациях, когда «первость» определяется не датой, а сложным набором условий (например: сначала записи со статусом «Активен», потом «В очереди», потом остальные), можно использовать расчетное поле приоритета.

Проанализируем ситуацию: нам нужно выбрать одну запись для каждого объекта, отдавая предпочтение определенным признакам.


ВЫБРАТЬ
    Т.Объект,
    ВЫБОР
        КОГДА Т.Статус = ЗНАЧЕНИЕ(Перечисление.Статусы.Важный) ТОГДА 3
        КОГДА Т.Статус = ЗНАЧЕНИЕ(Перечисление.Статусы.Обычный) ТОГДА 2
        ИНАЧЕ 1
    КОНЕЦ КАК ВесПриоритета,
    Т.Ссылка
ПОМЕСТИТЬ ВТ_Приоритеты
ИЗ
    Справочник.Задачи КАК Т
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ.Объект,
    МАКСИМУМ(ВТ.ВесПриоритета) КАК МаксВес
ПОМЕСТИТЬ ВТ_МаксВес
ИЗ
    ВТ_Приоритеты КАК ВТ
СГРУППИРОВАТЬ ПО
    ВТ.Объект
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ_Приоритеты.Объект,
    ВТ_Приоритеты.Ссылка
ИЗ
    ВТ_Приоритеты КАК ВТ_Приоритеты
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_МаксВес КАК ВТ_МаксВес
    ПО ВТ_Приоритеты.Объект = ВТ_МаксВес.Объект
    И ВТ_Приоритеты.ВесПриоритета = ВТ_МаксВес.МаксВес

Такой подход позволяет формализовать любые бизнес-правила выбора «лучшей» записи в группе.

Рекомендации по выбору метода

Выясним, какой метод лучше использовать в зависимости от обстоятельств:

Помните, что в языке запросов 1С порядок записей в результате не гарантирован, если не указано явное УПОРЯДОЧИТЬ ПО. Поэтому при определении «первой» записи всегда четко задавайте критерии сортировки, чтобы результат был детерминированным.

← На главную