Как в запросе 1С сделать количество колонок равным числу уникальных значений цен

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

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

В этой статье мы подробно разберем, как реализовать динамическое количество колонок, проанализируем различные подходы — от самого простого и правильного (использование СКД) до программных методов формирования запроса.

Способ 1. Использование системы компоновки данных (СКД) — Кросс-таблица

Это самый эффективный и рекомендуемый метод в современной платформе 1С. Система компоновки данных (СКД) обладает встроенным механизмом «разворота» данных, который избавляет программиста от написания сложного кода. Нам не нужно заранее знать количество колонок, СКД сделает все автоматически.

Разберем алгоритм настройки по шагам:

  1. Создадим стандартный запрос, который выбирает данные в «плоском» виде. В нашем случае это будут поля: Номенклатура, Склад, ВидЦены и Цена. Важно, чтобы ВидЦены был отдельным полем.
  2. Перейдем на вкладку Ресурсы и добавим поле Цена. В выражении укажем СУММА(Цена) или МАКСИМУМ(Цена), если мы уверены, что для одной номенклатуры может быть только одна цена одного вида.
  3. На вкладке Настройки добавим в структуру отчета элемент типа Таблица.
  4. В Строки таблицы добавим группировки Номенклатура и Склад.
  5. В Колонки таблицы добавим группировку ВидЦены.

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

Способ 2. Динамическое формирование текста запроса в коде

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

Рассмотрим пример кода, реализующий этот подход:


// Шаг 1: Получаем список всех доступных видов цен
ЗапросВидыЦен = Новый Запрос;
ЗапросВидыЦен.Текст = "ВЫБРАТЬ РАЗЛИЧНЫЕ ВидыЦен.Ссылка, ВидыЦен.Наименование КАК Имя КАК ВидЦены ИЗ Справочник.ВидыЦен КАК ВидыЦен";
ВыборкаЦен = ЗапросВидыЦен.Выполнить().Выбрать();

// Шаг 2: Формируем текст основного запроса динамически
ТекстЗапроса = "ВЫБРАТЬ
               |    ТоварыОстатки.Номенклатура КАК Номенклатура,
               |    ТоварыОстатки.Склад КАК Склад";

Индекс = 0;
Пока ВыборкаЦен.Следующий() Цикл
    // Очищаем имя колонки от пробелов и спецсимволов для корректности SQL
    ИмяКолонки = "Цена_" + СтрЗаменить(ВыборкаЦен.ВидЦены.УникальныйИдентификатор(), "-", "_");
    
    ТекстЗапроса = ТекстЗапроса + ",
                   | СУММА(ВЫБОР КОГДА Цены.ВидЦены = &ВидЦены" + Индекс + " ТОГДА Цены.Цена ИНАЧЕ 0 КОНЕЦ) КАК " + ИмяКолонки;
    Индекс = Индекс + 1;
КонецЦикла;

ТекстЗапроса = ТекстЗапроса + "
               | ИЗ РегистрНакопления.ТоварыНаСкладах.Остатки КАК ТоварыОстатки
               | ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних КАК Цены ПО ТоварыОстатки.Номенклатура = Цены.Номенклатура
               | СГРУППИРОВАТЬ ПО ТоварыОстатки.Номенклатура, ТоварыОстатки.Склад";

// Шаг 3: Установка параметров
Запрос = Новый Запрос(ТекстЗапроса);
ВыборкаЦен.Сбросить();
Индекс = 0;
Пока ВыборкаЦен.Следующий() Цикл
    Запрос.УстановитьПараметр("ВидЦены" + Индекс, ВыборкаЦен.ВидЦены);
    Индекс = Индекс + 1;
КонецЦикла;

Проанализируем ситуацию: в этом примере мы используем конструкцию ВЫБОР КОГДА ... ТОГДА для каждой колонки. Это классический способ реализации Pivot в SQL. Обратите внимание на важность типизации — все возвращаемые значения должны иметь один тип (Число), чтобы избежать проблем при группировке.

Способ 3. Постобработка в Таблице значений (Метод Свернуть)

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

Рассмотрим пошагово:

  1. Выполняем простой запрос, получая строки вида: Номенклатура | ВидЦены | Цена.
  2. Выгружаем результат в ТаблицаЗначений.
  3. Программно добавляем в эту таблицу новые колонки, соответствующие названиям видов цен.
  4. Заполняем эти колонки данными из строк.
  5. Используем метод Свернуть().

Посмотрим на пример реализации:


ТЗ = Запрос.Выполнить().Выгрузить(); // Имеет колонки Номенклатура, Склад, ВидЦены, Цена

// Добавляем новые колонки
Для Каждого СтрокаЦены Из СписокУникальныхЦен Цикл
    ИмяКолонки = "Цена_" + СтрокаЦены.Идентификатор;
    ТЗ.Колонки.Добавить(ИмяКолонки, Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2)));
КонецЦикла;

// Заполняем новые колонки
Для Каждого Стр Из ТЗ Цикл
    ИмяКолонки = "Цена_" + Стр.ВидЦены.Идентификатор;
    Стр[ИмяКолонки] = Стр.Цена;
КонецЦикла;

// Сворачиваем таблицу, перечисляя все новые колонки в ресурсах
СписокКолонокРесурсов = "Цена_Оптовая, Цена_Розничная, Цена_Закупочная"; // Формируется динамически
ТЗ.Свернуть("Номенклатура, Склад", СписокКолонокРесурсов);

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

Способ 4. Вывод в Табличный документ через области (Присоединить)

Если задача стоит не просто в обработке данных, а в красивом выводе отчета пользователю без использования СКД, мы можем использовать метод Присоединить() объекта ТабличныйДокумент.

Разберем логику: В макете создаются три основные области:

При выводе мы сначала выводим ШапкаЛево методом Вывести(), а затем в цикле по видам цен присоединяем ШапкаКолонки методом Присоединить(). Таким образом, шапка отчета будет расти вправо динамически. Аналогично поступаем со строками данных для каждой позиции товара.

Важные технические нюансы

При реализации любого из этих методов стоит учитывать ограничения платформы 1С:

Мы рассмотрели основные способы решения задачи. Выбор конкретного метода зависит от вашего контекста: для типовых отчетов выбирайте СКД, для интеграций и сложных обработок — динамическое формирование текста запроса, а для быстрого прототипирования — постобработку Таблицы Значений.

← На главную