Как сделать обратную сортировку в запросе 1С 7.7 и почему она может не работать?

Программист 1С v7.7 IT и автоматизация бизнеса
← На главную

При работе с запросами в 1С:Предприятие 7.7 часто возникает задача получить данные, отсортированные в обратном порядке (по убыванию). Стандартный механизм для этого — использование метода Запрос.Группировка() с параметром направления -1. Однако иногда разработчики сталкиваются с ситуацией, когда такой вызов возвращает пустую выборку, хотя прямой обход работает корректно. Разберем, почему так происходит и какие существуют способы решения этой проблемы.

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

Исходный код запроса:


Запрос = СоздатьОбъект("Запрос");
ТекстЗапроса = 
"//{{ЗАПРОС(Сформировать)
|Период с ПериодС по ПериодПО;
|ВладелецЗап = Справочник.ГрафКалендарь.Владелец;
|СпрЗап = Справочник.ГрафКалендарь.ТекущийЭлемент;
|Группировка СпрЗап Упорядочить по СпрЗап.ДатаРеестр без групп;
|Условие (СпрЗап.ПометкаУдаления()=0);
|Условие (Запрос.ВладелецЗап = Название);
|Без итогов;
|"//}}ЗАПРОС
;

// Этот цикл работает
Пока Запрос.Группировка() = 1 Цикл
    Сообщить("ДатаРеестр " + Запрос.СпрЗап.ДатаРеестр);
КонецЦикла;

// А такой вызов для обратной сортировки возвращает пустую выборку
// Пока Запрос.Группировка(1, -1) = 1 Цикл
//     ...
// КонецЦикла;

Основной вопрос: почему вызов Запрос.Группировка(1, -1) не работает, хотя синтаксически он верен? Причина кроется в одной из опций самого запроса.

Решение 1: Сортировка средствами запроса и опция "Без итогов"

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

Когда в тексте запроса присутствует директива Без итогов;, платформа использует оптимизированный, "облегченный" алгоритм получения данных. Этот алгоритм работает быстрее, но имеет ограничения: он не поддерживает некоторые возможности, включая обратный обход результата. Именно поэтому вызов Запрос.Группировка(1, -1) с опцией Без итогов не дает результата.

Чтобы заставить обратную сортировку работать, необходимо включить полновесный механизм обработки запроса. Сделаем это по шагам:

  1. Убираем директиву Без итогов; из текста запроса. Это активирует механизм расчета итогов, который корректно обрабатывает параметр направления сортировки.
  2. Используем метод Группировка() с двумя параметрами. Первый параметр — номер или имя группировки (в нашем случае она одна, поэтому ее номер 1). Второй параметр — направление обхода: 1 для прямого (по умолчанию) и -1 для обратного.

Исправленный вариант кода будет выглядеть так:


Запрос = СоздатьОбъект("Запрос");
ТекстЗапроса = 
"//{{ЗАПРОС(Сформировать)
|Период с ПериодС по ПериодПО;
|ВладелецЗап = Справочник.ГрафКалендарь.Владелец;
|СпрЗап = Справочник.ГрафКалендарь.ТекущийЭлемент;
|Группировка СпрЗап Упорядочить по СпрЗап.ДатаРеестр без групп;
|Условие (СпрЗап.ПометкаУдаления()=0);
|Условие (Запрос.ВладелецЗап = Название);
//|Без итогов; // <-- УБИРАЕМ ЭТУ СТРОКУ
|"//}}ЗАПРОС
;

Если Запрос.Выполнить(ТекстЗапроса) = 0 Тогда
    Возврат;
КонецЕсли;

// Теперь обратный обход будет работать
Пока Запрос.Группировка(1, -1) = 1 Цикл
    Сообщить("ДатаРеестр " + Запрос.СпрЗап.ДатаРеестр);
КонецЦикла;

Важный момент: после удаления опции Без итогов в результате запроса может появиться дополнительная итоговая строка по группировке. В цикле ее можно проигнорировать, если итоги не нужны для дальнейшей обработки.

Решение 2: Гибкий и надежный способ через ТаблицуЗначений

Хотя первый способ решает проблему, существует более универсальный и предсказуемый подход — выгрузка результата запроса в ТаблицуЗначений (ТЗ) и ее последующая сортировка. Этот метод является предпочтительным во многих случаях.

Преимущества этого подхода:

Разберем реализацию этого метода по шагам:

  1. Создаем запрос, который просто отбирает необходимые поля без указания сортировки.
  2. Выполняем запрос и выгружаем его результат в объект ТаблицаЗначений с помощью метода Запрос.Выгрузить().
  3. Сортируем полученную таблицу методом ТЗ.Сортировать(). Для сортировки по убыванию к имени колонки добавляется знак "минус" (-).
  4. Организуем цикл для перебора строк уже отсортированной таблицы.

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


// 1. Упрощаем запрос, убираем сортировку
Запрос = СоздатьОбъект("Запрос");
ТекстЗапроса = 
"//{{ЗАПРОС(Сформировать)
|ВладелецЗап = Справочник.ГрафКалендарь.Владелец;
|СпрЗап = Справочник.ГрафКалендарь.ТекущийЭлемент;
|ДатаРеестр = Справочник.ГрафКалендарь.ДатаРеестр;
|Группировка СпрЗап без групп; // Убрали 'Упорядочить по'
|Условие (ВладелецЗап = Конт); // Используем переменную Конт для условия
|Обрабатывать НеПомеченныеНаУдаление; // Более эффективная проверка
|"//}}ЗАПРОС
;

Если Запрос.Выполнить(ТекстЗапроса) = 0 Тогда
    Возврат;
КонецЕсли;

// 2. Выгружаем результат в ТаблицуЗначений
ТЗ = СоздатьОбъект("ТаблицаЗначений");
Запрос.Выгрузить(ТЗ);

// 3. Сортируем ТЗ по убыванию даты
ТЗ.Сортировать("ДатаРеестр-"); // Знак "-" означает обратный порядок

// 4. Перебираем отсортированную таблицу
ТЗ.ВыбратьСтроки();
Пока ТЗ.ПолучитьСтроку() = 1 Цикл
    Сообщить("ДатаРеестр " + ТЗ.ДатаРеестр);
КонецЦикла;

Дополнительные рекомендации по оптимизации запроса

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

В итоге, выбор между двумя способами сортировки зависит от задачи. Если нужна простая однократная сортировка и вы готовы отказаться от опции Без итогов, первый метод вполне подойдет. Однако для более сложных и гибких решений, а также для поддержания чистоты кода, рекомендуется использовать второй метод с выгрузкой данных в ТаблицуЗначений.

← На главную