Как вывести в запросе 1С все значения перечисления или списка, даже если их нет в итоговой таблице?

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

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

Метод 1: Использование конструкции ОБЪЕДИНИТЬ ВСЕ для создания «скелета»

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

Представим, что у нас есть документ АварийнаяЗаявка и нам нужно вывести данные по всем типам объектов (ААЗ): "ПРГ", "КШ", "РС", "ЗА". Если в базе нет заявок по объекту "ПРГ", обычный запрос его не покажет. Чтобы это исправить, мы объединяем реальные данные с «пустыми» константами.

Проанализируем структуру такого запроса:


ВЫБРАТЬ
    т.ААЗ КАК ААЗ,
    СУММА(т.Высокое1Кат) КАК Высокое1Кат,
    СУММА(т.Высокое2Кат) КАК Высокое2Кат,
    СУММА(т.Среднее) КАК Среднее,
    СУММА(т.Низкое) КАК Низкое
ИЗ (
    // Часть 1: Реальные данные из документов
    ВЫБРАТЬ
        АварийнаяЗаявка.Характеристика_2024.ААЗ КАК ААЗ,
        ВЫБОР КОГДА АварийнаяЗаявка.ФактическоеДавлениеГаза = ЗНАЧЕНИЕ(Перечисление.ДавлениеГаза.Высокое) ТОГДА 1 ИНАЧЕ 0 КОНЕЦ КАК Высокое1Кат,
        ВЫБОР КОГДА АварийнаяЗаявка.ФактическоеДавлениеГаза = ЗНАЧЕНИЕ(Перечисление.ДавлениеГаза.Высокое2) ТОГДА 1 ИНАЧЕ 0 КОНЕЦ КАК Высокое2Кат,
        ВЫБОР КОГДА АварийнаяЗаявка.ФактическоеДавлениеГаза = ЗНАЧЕНИЕ(Перечисление.ДавлениеГаза.Среднее) ТОГДА 1 ИНАЧЕ 0 КОНЕЦ КАК Среднее,
        ВЫБОР КОГДА АварийнаяЗаявка.ФактическоеДавлениеГаза = ЗНАЧЕНИЕ(Перечисление.ДавлениеГаза.Низкое) ТОГДА 1 ИНАЧЕ 0 КОНЕЦ КАК Низкое
    ИЗ
        Документ.АварийнаяЗаявка КАК АварийнаяЗаявка

    ОБЪЕДИНИТЬ ВСЕ

    // Часть 2: Наполнение «скелета» нулевыми строками
    ВЫБРАТЬ "ПРГ", 0, 0, 0, 0
    ОБЪЕДИНИТЬ ВСЕ
    ВЫБРАТЬ "КШ", 0, 0, 0, 0
    ОБЪЕДИНИТЬ ВСЕ
    ВЫБРАТЬ "РС", 0, 0, 0, 0
    ОБЪЕДИНИТЬ ВСЕ
    ВЫБРАТЬ "ЗА", 0, 0, 0, 0
) КАК т

СГРУППИРОВАТЬ ПО
    т.ААЗ

В данном примере во вложенном запросе мы сначала выбираем фактические данные, а затем добавляем строки с жестко заданными именами объектов и нулевыми значениями ресурсов. После чего внешняя секция СГРУППИРОВАТЬ ПО схлопывает все данные, и если по объекту "ПРГ" не было документов, в итоговой таблице останется строка из второй части объединения с нулями.

Метод 2: Использование Временных Таблиц и Левого соединения

Метод с ОБЪЕДИНИТЬ ВСЕ удобен, когда значений мало. Однако если список категорий велик или хранится в справочнике, более профессиональным решением будет использование Временной Таблицы (ВТ). Это сделает код более читаемым и легким в поддержке (Добавление данных в существующую временную таблицу - использование и проблемы).

Разберем, как это реализовать:

  1. Создадим временную таблицу ВТ_ВсеОбъекты, содержащую полный перечень нужных нам значений ААЗ.
  2. Выполним ЛЕВОЕ СОЕДИНЕНИЕ этой таблицы с таблицей фактических данных.
  3. Используем функцию ЕСТЬNULL, чтобы превратить пустые ячейки в ноль.

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


// 1. Формируем эталонный список
ВЫБРАТЬ "ПРГ" КАК ААЗ ПОМЕСТИТЬ ВТ_Скелет ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ "КШ" ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ "РС" ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ "ЗА";

// 2. Основной запрос
ВЫБРАТЬ
    Скелет.ААЗ КАК ААЗ,
    ЕСТЬNULL(СУММА(Данные.Количество), 0) КАК Итого
ИЗ
    ВТ_Скелет КАК Скелет
ЛЕВОЕ СОЕДИНЕНИЕ (
    ВЫБРАТЬ
        АварийнаяЗаявка.Характеристика_2024.ААЗ КАК ААЗ,
        1 КАК Количество
    ИЗ
        Документ.АварийнаяЗаявка КАК АварийнаяЗаявка
) КАК Данные
ПО Скелет.ААЗ = Данные.ААЗ
СГРУППИРОВАТЬ ПО
    Скелет.ААЗ

Важный момент: использование ЛЕВОЕ СОЕДИНЕНИЕ гарантирует, что все строки из таблицы Скелет попадут в результат, независимо от того, нашлись ли для них соответствия в таблице Данные. Функция ЕСТЬNULL крайне важна, так как при отсутствии данных в правой таблице результатом будет NULL, а для корректного отчета нам нужно число 0.

Метод 3: Возможности Системы Компоновки Данных (СКД)

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

Рассмотрим ключевые настройки СКД для этой задачи:

Выясним причину, почему разработчики часто предпочитают СКД: она позволяет динамически менять список группировок, не переписывая текст запроса с бесконечными ОБЪЕДИНИТЬ ВСЕ.

Метод 4: Работа с Перечислениями напрямую

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

  1. Передать массив всех значений перечисления в запрос через параметр: Запрос.УстановитьПараметр("МассивВсехЗначений", Перечисления.ДавлениеГаза.Выгрузить()).
  2. Создать временную таблицу на основе этого массива и уже к ней «левым соединением» цеплять фактические данные.

Пример программной подготовки «скелета» для запроса:


СписокЗначений = Новый Массив;
Для Каждого ЗначениеИзПеречисления Из Перечисления.ДавлениеГаза Цикл
    СписокЗначений.Добавить(ЗначениеИзПеречисления);
КонецЦикла;

Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ Ссылка КАК Значение ПОМЕСТИТЬ ВТ_ВсеЗначения ИЗ &МассивПеречислений КАК Массив;
    |... далее левое соединение с ВТ_ВсеЗначения ...";
Запрос.УстановитьПараметр("МассивПеречислений", СписокЗначений);

Оптимизация производительности: ЕСТЬNULL vs ВЫБОР

Проанализируем ситуацию с точки зрения производительности СУБД. В итоговом ответе на форуме автор использовал конструкцию СУММА(ВЫБОР КОГДА ... ТОГДА 1 ИНАЧЕ 0 КОНЕЦ). Это рабочая схема, которая хорошо подходит для создания «кросс-таблиц» (разворота строк в колонки).

Однако, если вы используете ЛЕВОЕ СОЕДИНЕНИЕ, предпочтительнее использовать ЕСТЬNULL(Поле, 0). На уровне SQL-сервера эта функция обычно отрабатывает быстрее, чем логическое ветвление CASE WHEN. Также старайтесь избегать фильтрации в секции ГДЕ по полям правой таблицы при левом соединении, так как это фактически превращает его во ВНУТРЕННЕЕ СОЕДИНЕНИЕ, и ваши пустые строки снова исчезнут. Условия на правую таблицу нужно выносить в секцию ПО (связи таблиц).

Подведем итог: для решения задачи «пустых строк» лучше всего подходит метод создания временной таблицы-скелета с последующим левым соединением или механизмы дополнения значений в СКД. Это гарантирует масштабируемость вашего кода и корректность отображения данных для пользователя.

← На главную