Как убрать тип NULL из колонок таблицы значений после запроса и обеспечить автоматическое заполнение нулями

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

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

Почему возникает проблема с типом NULL

Рассмотрим ситуацию подробнее. Когда мы выполняем запрос и используем метод Выгрузить(), платформа формирует колонки таблицы значений на основе метаданных источника. Если поле в базе данных может принимать значение NULL (например, из-за левого соединения или особенностей хранения в СУБД), 1С перестраховывается и включает NULL в ОписаниеТипов колонки. Даже явное приведение в запросе через ЕСТЬNULL не меняет «мнение» платформы о составе типов будущей колонки, так как типизация строится на этапе компиляции запроса исходя из структуры таблиц.

Выясним причину, почему это неудобно. Если в описании типов колонки разрешен NULL, то при создании новой строки методом Строка = ТЗ.Добавить(), платформа всегда отдает приоритет NULL как значению по умолчанию. Если бы в колонке был разрешен только тип Число, система автоматически подставила бы 0.

Решение 1. Использование метода Загрузить() в заранее подготовленную таблицу

Это самый «чистый» и рекомендуемый способ. Вместо того чтобы позволять запросу создавать таблицу «с нуля», мы сами подготовим структуру с нужными нам типами, а затем просто перекачаем туда данные. Разберем этот метод по шагам.

Сначала мы создаем объект ТаблицаЗначений и программно добавляем в него колонки, строго указывая ОписаниеТипов без участия NULL.


// 1. Создаем пустую таблицу
ТЗ = Новый ТаблицаЗначений;

// 2. Описываем типы (только Число, без NULL)
МассивТипов = Новый Массив;
МассивТипов.Добавить(Тип("Число"));
КвалификаторыЧисла = Новый КвалификаторыЧисла(15, 2);
ОписаниеТипаЧисло = Новый ОписаниеТипов(МассивТипов, , , КвалификаторыЧисла);

// 3. Добавляем колонки в ТЗ
ТЗ.Колонки.Добавить("Сумма", ОписаниеТипаЧисло);
ТЗ.Колонки.Добавить("Количество", ОписаниеТипаЧисло);

// 4. Выполняем запрос
РезультатЗапроса = Запрос.Выполнить();

// 5. Загружаем данные
// Метод Загрузить() сопоставит имена колонок и попытается привести типы
ТЗ.Загрузить(РезультатЗапроса.Выгрузить());

Проанализируем ситуацию: теперь, когда мы вызовем НоваяСтрока = ТЗ.Добавить(), значение НоваяСтрока.Сумма автоматически станет равным 0. Платформа видит, что NULL в данной колонке запрещен, и использует единственно возможное «пустое» значение для типа Число.

Решение 2. Программное пересоздание колонок («Кокнуть и создать»)

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


// Допустим, ТЗ уже получена из запроса и имеет колонку "Цена" с типом "Число, NULL"
// Нам нужно оставить только "Число"

// 1. Создаем правильное описание типов
МассивТипов = Новый Массив;
МассивТипов.Добавить(Тип("Число"));
ОписаниеТипаЧисло = Новый ОписаниеТипов(МассивТипов);

// 2. Добавляем временную колонку с нужным типом
ТЗ.Колонки.Добавить("Цена_Временная", ОписаниеТипаЧисло);

// 3. Копируем данные (платформа сама преобразует NULL в 0 при копировании в типизированную колонку)
ТЗ.ЗаполнитьЗначения(0, "Цена_Временная"); // на всякий случай инициализируем, хотя это не обязательно
Для Каждого Стр Из ТЗ Цикл
    Стр.Цена_Временная = Стр.Цена;
КонецЦикла;

// 4. Удаляем старую колонку и переименовываем новую
ТЗ.Колонки.Удалить("Цена");
ТЗ.Колонки.Цена_Временная.Имя = "Цена";
ТЗ.Колонки.Цена_Временная.Заголовок = "Цена";

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

Решение 3. Использование типизированного массива в конструкторе ОписаниеТипов

Разберем подробнее, как работает конструктор ОписаниеТипов. Если вы хотите гарантированно исключить NULL, всегда передавайте в конструктор массив типов. Рассмотрим разницу:

Неправильно:
ОписаниеТипов = Новый ОписаниеТипов("Число"); — в некоторых контекстах платформа может добавить NULL по умолчанию, если объект создается на основе метаданных.

Правильно:


МассивТипов = Новый Массив;
МассивТипов.Добавить(Тип("Число"));
// Используем конструктор, где второй параметр (изъятые типы) пуст, 
// а в первом массиве только нужные нам типы
ОписаниеТипов = Новый ОписаниеТипов(МассивТипов);

Если мы создадим колонку таким образом, в нее физически нельзя будет записать NULL. Любая попытка записи NULL (например, при загрузке данных из другого источника) приведет к автоматическому преобразованию значения в 0.

Почему ЕСТЬNULL в запросе не помогает с описанием типов?

Многие программисты задаются вопросом: «Я ведь написал ЕСТЬNULL(Поле, 0), почему в ТЗ все равно есть NULL?». Нужно понимать различие между значением и типом.

Поэтому ручная типизация в коде 1С после выполнения запроса — это не «колхоз», как выразился автор темы, а осознанная необходимость управления типами в динамических структурах данных.

Подведем итоги

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

  1. Если структура таблицы известна заранее — всегда создавайте ТЗ вручную с жесткой типизацией через ОписаниеТипов(МассивТипов) и используйте ТЗ.Загрузить(РезультатЗапроса.Выгрузить()).
  2. Если таблица формируется динамически и вам мешает NULL — используйте метод с подменой колонок, создавая новую колонку с правильным типом и удаляя старую.
  3. Помните, что наличие типа NULL в колонке — это не ошибка, а особенность работы 1С с метаданными запросов. Однако избавление от него значительно упрощает код, избавляя от необходимости вручную присваивать Строка.Значение = 0 при каждом добавлении данных.

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

← На главную