Что быстрее в запросах 1С: ВЫБРАТЬ ПЕРВЫЕ 1 с сортировкой или МАКСИМУМ?

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

При разработке запросов в системе 1С:Предприятие программисты часто сталкиваются с задачей получения самого свежего документа или последнего значения какого-либо показателя. Традиционно существует два основных подхода: использование конструкции ВЫБРАТЬ ПЕРВЫЕ 1 с обратной сортировкой и применение агрегатной функции МАКСИМУМ. На первый взгляд может показаться, что это взаимозаменяемые вещи, однако при детальном анализе выясняется, что между ними есть существенные различия как в производительности, так и в логике обработки данных. Разберем эти подходы подробно.

Вариант 1: Использование конструкции ВЫБРАТЬ ПЕРВЫЕ 1

Рассмотрим пример запроса, который выбирает дату самого последнего созданного акта об оказании производственных услуг:


ВЫБРАТЬ ПЕРВЫЕ 1
    АктОбОказанииПроизводственныхУслуг.Дата КАК Дата
ИЗ
    Документ.АктОбОказанииПроизводственныхУслуг КАК АктОбОказанииПроизводственныхУслуг
УПОРЯДОЧИТЬ ПО
    Дата УБЫВ

Проанализируем ситуацию: как этот запрос будет выполняться на уровне СУБД (например, MS SQL Server). Многие разработчики опасаются, что использование УПОРЯДОЧИТЬ ПО заставит систему выполнять тяжелую операцию сортировки всех записей в таблице. Однако это не так, если по полю Дата существует индекс. В платформе 1С для документов поле Дата индексируется автоматически.

В этом случае оптимизатор SQL-сервера не будет считывать все данные и сортировать их. Он выполнит операцию Index Backward Scan (обратное сканирование индекса). Система просто обратится к индексу, перейдет к его последнему элементу и возьмет первую строку. Это происходит мгновенно даже на таблицах с миллионами записей.

Вариант 2: Использование агрегатной функции МАКСИМУМ

Теперь посмотрим на альтернативный вариант с использованием агрегации:


ВЫБРАТЬ
    МАКСИМУМ(АктОбОказанииПроизводственныхУслуг.Дата) КАК Дата
ИЗ
    Документ.АктОбОказанииПроизводственныхУслуг КАК АктОбОказанииПроизводственныхУслуг

В данном случае мы просим систему вычислить максимальное значение. На первый взгляд, это кажется более логичным для поиска одного числа. С точки зрения SQL-сервера, этот запрос также будет использовать индекс (если он есть). План выполнения будет содержать оператор Stream Aggregate. СУБД также обратится к индексу и получит максимальное значение через чтение последней страницы индекса.

Сравнение производительности на практике

Проанализируем результаты реальных тестов. На регистрах накопления объемом более 10 миллионов записей оба варианта показывают практически идентичное время выполнения — в среднем от 0.004 до 0.006 секунд. Это подтверждает, что при наличии правильных индексов оптимизатор запросов СУБД сводит обе конструкции к максимально эффективному плану чтения.

Однако важно понимать, что ВЫБРАТЬ ПЕРВЫЕ 1 может быть чуть эффективнее в плане ресурсов процессора, так как оператору Top не нужно выполнять логику агрегации, он просто прекращает выполнение после получения первой строки. Но в масштабах обычных задач 1С эта разница ничтожна — так называемая "оптимизация на спичках".

Критическое различие: пустые таблицы и NULL

Выясним причину, по которой выбор между этими двумя запросами может привести к ошибкам в коде. Основное различие кроется в том, что возвращает запрос, если в таблице нет данных:

  1. Агрегатная функция МАКСИМУМ: Если таблица пуста, запрос всегда возвращает одну строку, где в поле результата будет значение NULL. В коде 1С при чтении такого результата через Выборка.Следующий() вы получите Истина, а в значении поля будет NULL. Если вы попытаетесь прибавить к этому значению число или использовать его там, где ожидается тип Дата, программа выдаст ошибку.
  2. Конструкция ПЕРВЫЕ 1: Если данных нет, запрос вернет пустую выборку (0 строк). Метод Результат.Пустой() вернет Истина, а цикл Пока Выборка.Следующий() Цикл просто не выполнится ни разу. Это поведение часто считается более безопасным и предсказуемым для логики на языке 1С.

Особенности работы в Системе компоновки данных (СКД)

Рассмотрим еще один важный нюанс, связанный с использованием этих конструкций в отчетах. Использование ВЫБРАТЬ ПЕРВЫЕ 1 внутри текста запроса для набора данных СКД иногда может приводить к неожиданным результатам или ошибкам. Это связано с тем, что СКД оборачивает ваш запрос во вложенные подзапросы для наложения пользовательских настроек и отборов.

Если вы используете ПЕРВЫЕ 1, ограничение может сработать до того, как применятся пользовательские фильтры, что приведет к неверным данным в отчете. В контексте построения отчетов использование МАКСИМУМ с последующей группировкой считается более надежным и "правильным" методом, так как агрегаты корректно обрабатываются механизмом компоновки.

Масштабируемость запроса

Проанализируем ситуацию, когда нам нужно получить не только дату последнего документа, но и сам этот документ (ссылку).

Если мы используем ВЫБРАТЬ ПЕРВЫЕ 1, нам достаточно просто добавить поле Ссылка в список выбора:


ВЫБРАТЬ ПЕРВЫЕ 1
    Акт.Дата КАК Дата,
    Акт.Ссылка КАК Ссылка
ИЗ
    Документ.АктОбОказанииПроизводственныхУслуг КАК Акт
УПОРЯДОЧИТЬ ПО
    Дата УБЫВ

Это никак не усложнит выполнение запроса. Если же мы решим использовать МАКСИМУМ, нам придется либо использовать СГРУППИРОВАТЬ ПО для ссылки (что вернет нам все документы, а не последний), либо строить сложный вложенный запрос с соединением. Таким образом, ВЫБРАТЬ ПЕРВЫЕ 1 гораздо гибче, когда требуется получить несколько полей из "последней" записи.

Итоги и рекомендации

Подводя итог нашему анализу, можно выделить следующие рекомендации:

Рассмотренные примеры показывают, что выбор между этими конструкциями — это не вопрос скорости, а вопрос удобства дальнейшей обработки данных в коде 1С и архитектурной чистоты вашего решения.

← На главную