При работе с отчетами в системе 1С:Предприятие 7.7 программисты часто сталкиваются с необходимостью вывода данных в определенном порядке. Типичная задача — построение рейтинга продаж (поможет управленческая отчетность и консолидация для 1С 7.7), где в начале списка должен находиться покупатель с наибольшим оборотом, а далее список должен идти по убыванию суммы. Несмотря на кажущуюся простоту, штатный язык запросов «семерки» имеет свои нюансы в реализации этой задачи.
В этой статье мы подробно разберем, почему стандартная конструкция Упорядочить по сама по себе может не давать нужного результата, и рассмотрим три надежных способа решения проблемы: от классического использования параметров метода Группировка до продвинутых техник с применением таблиц значений и прямых SQL-запросов.
Первым делом проанализируем ситуацию, с которой сталкивается большинство разработчиков. При написании текста запроса мы указываем ключевое слово Упорядочить по, однако при обходе результатов система по умолчанию выдает данные по возрастанию. Секрет заключается в том, что управление направлением сортировки в 1С 7.7 разделено между текстом запроса и кодом обхода результата.
Шаг 1. Подготовка текста запроса. Чтобы система вообще начала подготавливать данные для сортировки по значению функции (например, по сумме), необходимо явно указать это в тексте запроса. Если этого не сделать, никакие дальнейшие манипуляции в коде не помогут.
Рассмотрим правильный фрагмент текста запроса (для предварительной отладки может пригодиться читалка регистров):
ТекстЗапроса = "
|Период с ВыбНачПериода по ВыбКонПериода;
|Покупатель = Регистр.Продажи.Покупатель;
|Сумма1 = Регистр.Продажи.ПродСтоимость;
|Функция СуммаИтог = Сумма(Сумма1);
|Группировка Покупатель без групп Упорядочить по СуммаИтог;";
Шаг 2. Обход результатов с параметром направления. Теперь самое важное. Метод Группировка(), который мы используем в цикле Пока, принимает два основных параметра. Рассмотрим их подробнее:
1 означает возрастание (по умолчанию), а значение -1 — убывание.Посмотрим на финальный программный код, который решает задачу:
Запрос = СоздатьОбъект("Запрос");
Если Запрос.Выполнить(ТекстЗапроса) = 0 Тогда
Возврат;
КонецЕсли;
// Здесь мы указываем -1 для сортировки по убыванию
Пока Запрос.Группировка(1, -1) = 1 Цикл
// В этом цикле Покупатели будут выходить от большего к меньшему по СуммаИтог
Сообщить("Покупатель: " + Запрос.Покупатель + " - Сумма: " + Запрос.СуммаИтог);
КонецЦикла;
Важное замечание: Если в тексте запроса вы забудете написать Упорядочить по СуммаИтог, то вызов Запрос.Группировка(1, -1) проигнорирует минус единицу и выведет данные в стандартном порядке (обычно по наименованию или внутреннему ID объекта).
Проанализируем ситуацию, когда штатный запрос работает недостаточно гибко. Например, если вам нужно отсортировать данные сначала по убыванию суммы, а для покупателей с одинаковой суммой — по алфавиту. В штатном запросе управлять направлением для каждого поля отдельно крайне сложно.
В этом случае наиболее эффективным методом будет выгрузка результата запроса в объект ТаблицаЗначений (здесь может пригодиться универсальный отчет по таблице значений). Рассмотрим этот алгоритм по шагам:
Упорядочить по (хотя его можно оставить для предварительной обработки).Выгрузить().Сортировать() объекта ТЗ.Пример реализации данного подхода (также полезна настраиваемая распечатка таблицы значений):
ТЗ = СоздатьОбъект("ТаблицаЗначений");
// Выгружаем результаты запроса в таблицу
// Второй параметр 1 означает, что мы выгружаем группировки
Запрос.Выгрузить(ТЗ, 1, 0);
// Сортируем таблицу.
// Знак "-" после названия колонки означает убывание.
// Если знака нет или стоит "+", то сортировка идет по возрастанию.
ТЗ.Сортировать("СуммаИтог-, Покупатель+");
// Теперь обходим готовую таблицу
ТЗ.ВыбратьСтроки();
Пока ТЗ.ПолучитьСтроку() = 1 Цикл
// Выводим данные в печатную форму или отчет
// Доступ к данным осуществляется через колонки ТЗ
КонецЦикла;
Преимущества этого способа:
Выясним причину, по которой опытные разработчики часто отказываются от штатных запросов 1С 7.7. На больших объемах данных (базы размером в десятки гигабайт) штатный движок запросов начинает работать крайне медленно. Это происходит потому, что 1С сначала копирует данные во временные файлы DBF, производит расчеты и только потом сортирует.
Если ваша база работает на Microsoft SQL Server, вы можете использовать внешнюю компоненту 1C++ для написания «прямых» запросов. Рассмотрим, как выглядит решение нашей задачи на языке SQL:
// Пример упрощенного SQL-запроса через 1С++
ТекстЗапроса = "
|SELECT
| $Рег.Покупатель as [Покупатель $Справочник.Клиенты],
| SUM($Рег.ПродСтоимость) as СуммаИтог
|FROM
| $Регистр.Продажи as Рег
|WHERE
| Рег.PERIOD BETWEEN :НачДата AND :КонДата
|GROUP BY
| $Рег.Покупатель
|ORDER BY
| СуммаИтог DESC
|";
В данном случае ключевое слово DESC (descending) в конструкции ORDER BY отвечает за сортировку по убыванию на уровне самого SQL-сервера. Это решение работает в десятки, а иногда и в сотни раз быстрее штатных средств, так как сервер БД оптимизирует выполнение задачи на уровне своих внутренних алгоритмов.
Подведем итог и выясним, какой метод выбрать в зависимости от условий:
Проанализировав все способы, мы видим, что 1С 7.7 предоставляет достаточно инструментов для решения задачи упорядочивания. Главное — помнить о необходимости синхронизации текста запроса и параметров метода обхода группировок.