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

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

В процессе разработки часто возникает ситуация, когда после объединения нескольких источников данных или выполнения запроса получается «разреженная» ТаблицаЗначений. В такой таблице данные, относящиеся к одному объекту, разнесены по разным строкам: в одной строке заполнены одни колонки, а в другой — другие, при этом незаполненные поля содержат NULL или пустые ссылки. Стандартный метод Свернуть() в таких случаях часто не дает ожидаемого результата, так как платформа воспринимает NULL как отдельное уникальное значение и не объединяет такие строки.

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

Причина проблемы: почему метод Свернуть не работает?

Проанализируем механику работы метода Свернуть(КолонкиГруппировки, КолонкиСуммирования). Он объединяет строки только в том случае, если значения в колонках группировки полностью идентичны по типу и значению. Для платформы 1С значение NULL не равно пустой строке, числу 0 или пустой ссылке. Более того, если NULL попадает в колонку, по которой должно идти суммирование, метод может отработать некорректно, так как математические операции с NULL в коде (вне языка запросов) недопустимы.

Разберем по шагам, как можно решить эту задачу различными методами.

Способ 1. Использование агрегатных функций в запросе

Это самый надежный и производительный способ. Если ваша таблица значений уже сформирована, мы можем передать ее обратно в запрос в качестве параметра, поместить во временную таблицу и выполнить группировку средствами SQL. Главный секрет здесь — использование функций МАКСИМУМ или МИНИМУМ.

Выясним, почему это работает: в языке запросов 1С агрегатные функции (кроме КОЛИЧЕСТВО(*)) игнорируют значения NULL. Таким образом, если в одной строке у вас есть «Номенклатура 1», а во второй строке в этом же поле стоит NULL, функция МАКСИМУМ выберет именно «Номенклатура 1».

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


// Предположим, ИсходнаяТЗ — это ваша таблица с пропусками
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;

// Обязательно типизируем колонки при передаче ТЗ в запрос
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ТЗ.Поле1 КАК Поле1,
    |	ТЗ.Поле2 КАК Поле2,
    |	ТЗ.Поле3 КАК Поле3,
    |	ТЗ.Поле4 КАК Поле4,
    |	ТЗ.Поле5 КАК Поле5,
    |	ТЗ.Поле6 КАК Поле6
    |ПОМЕСТИТЬ ВТ_Данные
    |ИЗ
    |	&ТЗ КАК ТЗ
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |	ВТ_Данные.Поле1 КАК Поле1,
    |	ВТ_Данные.Поле2 КАК Поле2,
    |	МАКСИМУМ(ВТ_Данные.Поле3) КАК Поле3,
    |	МАКСИМУМ(ВТ_Данные.Поле4) КАК Поле4,
    |	МАКСИМУМ(ВТ_Данные.Поле5) КАК Поле5,
    |	МАКСИМУМ(ВТ_Данные.Поле6) КАК Поле6
    |ИЗ
    |	ВТ_Данные КАК ВТ_Данные
    |ГРУППИРОВАТЬ ПО
    |	ВТ_Данные.Поле1,
    |	ВТ_Данные.Поле2";

Запрос.УстановитьПараметр("ТЗ", ИсходнаяТЗ);
РезультатТЗ = Запрос.Выполнить().Выгрузить();

Этот метод позволяет эффективно «склеить» строки по ключевым полям (Поле1 и Поле2), собрав все значимые данные из остальных колонок.

Способ 2. Предварительная нормализация данных (ЕСТЬNULL)

Если вы формируете таблицу запросом, постарайтесь избежать появления NULL еще на этапе его выполнения. Для этого используйте функцию ЕСТЬNULL(Значение, Замена). Рассмотрим ситуацию: если поле должно содержать число, заменяйте NULL на 0; если ссылку — на пустую ссылку нужного типа.

Пример в запросе:


ВЫБРАТЬ
    ЕСТЬNULL(Таблица.Сумма, 0) КАК Сумма,
    ЕСТЬNULL(Таблица.Номенклатура, ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)) КАК Номенклатура

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

Способ 3. Программный обход с использованием Соответствия (Map)

В случаях, когда использование запроса невозможно или нецелесообразно (например, при очень маленьких объемах данных или сложной логике заполнения), можно прибегнуть к ручному обходу таблицы с использованием объекта Соответствие. Этот способ значительно быстрее, чем использование метода НайтиСтроки() внутри цикла.

Разберем этот алгоритм:

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

Пример реализации:


КэшСтрок = Новый Соответствие;
РезультатТЗ = ИсходнаяТЗ.СкопироватьКолонки();

Для Каждого СтрДанные Из ИсходнаяТЗ Цикл
    // Формируем ключ по полям группировки (Реквизит1 и Реквизит2)
    Ключ = Новый Структура("Рекв1, Рекв2", СтрДанные.Рекв1, СтрДанные.Рекв2);
    
    ЦелеваяСтрока = КэшСтрок.Получить(Ключ);
    Если ЦелеваяСтрока = Неопределено Тогда
        ЦелеваяСтрока = РезультатТЗ.Добавить();
        ЗаполнитьЗначенияСвойств(ЦелеваяСтрока, СтрДанные);
        КэшСтрок.Вставить(Ключ, ЦелеваяСтрока);
    Иначе
        // Дозаполняем только те поля, которые в целевой строке еще пустые
        Если Не ЗначениеЗаполнено(ЦелеваяСтрока.Поле3) Тогда
            ЦелеваяСтрока.Поле3 = СтрДанные.Поле3;
        КонецЕсли;
        // Повторяем для всех необходимых колонок
    КонецЕсли;
КонецЦикла;

Важные рекомендации

Проанализируем важные нюансы, которые помогут избежать ошибок при сворачивании данных:

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

← На главную