При работе с платформой 1С:Предприятие 8 разработчики часто сталкиваются с особенностью: даже если в тексте запроса мы используем функцию ЕСТЬNULL(Поле, 0), результирующая ТаблицаЗначений все равно содержит тип NULL в описании своих колонок. Это приводит к тому, что при выполнении метода Добавить() для создания новой строки, числовые колонки инициализируются значением NULL, а не нулем. Это заставляет нас писать лишний код для инициализации каждого реквизита. В этой статье мы подробно разберем, почему так происходит и как правильно настроить типизацию колонок.
Рассмотрим ситуацию подробнее. Когда мы выполняем запрос и используем метод Выгрузить(), платформа формирует колонки таблицы значений на основе метаданных источника. Если поле в базе данных может принимать значение NULL (например, из-за левого соединения или особенностей хранения в СУБД), 1С перестраховывается и включает NULL в ОписаниеТипов колонки. Даже явное приведение в запросе через ЕСТЬNULL не меняет «мнение» платформы о составе типов будущей колонки, так как типизация строится на этапе компиляции запроса исходя из структуры таблиц.
Выясним причину, почему это неудобно. Если в описании типов колонки разрешен NULL, то при создании новой строки методом Строка = ТЗ.Добавить(), платформа всегда отдает приоритет NULL как значению по умолчанию. Если бы в колонке был разрешен только тип Число, система автоматически подставила бы 0.
Это самый «чистый» и рекомендуемый способ. Вместо того чтобы позволять запросу создавать таблицу «с нуля», мы сами подготовим структуру с нужными нам типами, а затем просто перекачаем туда данные. Разберем этот метод по шагам.
Сначала мы создаем объект ТаблицаЗначений и программно добавляем в него колонки, строго указывая ОписаниеТипов без участия NULL.
// 1. Создаем пустую таблицу
ТЗ = Новый ТаблицаЗначений;
// 2. Описываем типы (только Число, без NULL)
МассивТипов = Новый Массив;
МассивТипов.Добавить(Тип("Число"));
КвалификаторыЧисла = Новый КвалификаторыЧисла(15, 2);
ОписаниеТипаЧисло = Новый ОписаниеТипов(МассивТипов, , , КвалификаторыЧисла);
// 3. Добавляем колонки в ТЗ
ТЗ.Колонки.Добавить("Сумма", ОписаниеТипаЧисло);
ТЗ.Колонки.Добавить("Количество", ОписаниеТипаЧисло);
// 4. Выполняем запрос
РезультатЗапроса = Запрос.Выполнить();
// 5. Загружаем данные
// Метод Загрузить() сопоставит имена колонок и попытается привести типы
ТЗ.Загрузить(РезультатЗапроса.Выгрузить());
Проанализируем ситуацию: теперь, когда мы вызовем НоваяСтрока = ТЗ.Добавить(), значение НоваяСтрока.Сумма автоматически станет равным 0. Платформа видит, что NULL в данной колонке запрещен, и использует единственно возможное «пустое» значение для типа Число.
Иногда структура запроса слишком сложна или динамична, чтобы описывать таблицу значений заранее. В этом случае мы можем изменить типы колонок уже после выгрузки. Однако стоит помнить, что свойство ТипЗнч у колонки доступно только для чтения. Поэтому мы применим прием с заменой колонки. Посмотрим на пример кода:
// Допустим, ТЗ уже получена из запроса и имеет колонку "Цена" с типом "Число, NULL"
// Нам нужно оставить только "Число"
// 1. Создаем правильное описание типов
МассивТипов = Новый Массив;
МассивТипов.Добавить(Тип("Число"));
ОписаниеТипаЧисло = Новый ОписаниеТипов(МассивТипов);
// 2. Добавляем временную колонку с нужным типом
ТЗ.Колонки.Добавить("Цена_Временная", ОписаниеТипаЧисло);
// 3. Копируем данные (платформа сама преобразует NULL в 0 при копировании в типизированную колонку)
ТЗ.ЗаполнитьЗначения(0, "Цена_Временная"); // на всякий случай инициализируем, хотя это не обязательно
Для Каждого Стр Из ТЗ Цикл
Стр.Цена_Временная = Стр.Цена;
КонецЦикла;
// 4. Удаляем старую колонку и переименовываем новую
ТЗ.Колонки.Удалить("Цена");
ТЗ.Колонки.Цена_Временная.Имя = "Цена";
ТЗ.Колонки.Цена_Временная.Заголовок = "Цена";
Важный момент: Использование цикла для копирования данных может быть медленным на очень больших таблицах (сотни тысяч строк). В таких случаях лучше использовать метод Загрузить(), описанный в первом решении.
Разберем подробнее, как работает конструктор ОписаниеТипов. Если вы хотите гарантированно исключить NULL, всегда передавайте в конструктор массив типов. Рассмотрим разницу:
Неправильно:
ОписаниеТипов = Новый ОписаниеТипов("Число"); — в некоторых контекстах платформа может добавить NULL по умолчанию, если объект создается на основе метаданных.
Правильно:
МассивТипов = Новый Массив;
МассивТипов.Добавить(Тип("Число"));
// Используем конструктор, где второй параметр (изъятые типы) пуст,
// а в первом массиве только нужные нам типы
ОписаниеТипов = Новый ОписаниеТипов(МассивТипов);
Если мы создадим колонку таким образом, в нее физически нельзя будет записать NULL. Любая попытка записи NULL (например, при загрузке данных из другого источника) приведет к автоматическому преобразованию значения в 0.
Многие программисты задаются вопросом: «Я ведь написал ЕСТЬNULL(Поле, 0), почему в ТЗ все равно есть NULL?». Нужно понимать различие между значением и типом.
ЕСТЬNULL гарантирует, что в результате (строках) запроса не будет значений NULL.NULL, так как платформа 1С строит описание типов колонки на основе потенциальной возможности появления NULL в исходной таблице базы данных.Поэтому ручная типизация в коде 1С после выполнения запроса — это не «колхоз», как выразился автор темы, а осознанная необходимость управления типами в динамических структурах данных.
Для эффективной работы с таблицами значений и автоматической инициализации полей при добавлении строк, мы рекомендуем придерживаться следующей стратегии:
ОписаниеТипов(МассивТипов) и используйте ТЗ.Загрузить(РезультатЗапроса.Выгрузить()).NULL — используйте метод с подменой колонок, создавая новую колонку с правильным типом и удаляя старую.NULL в колонке — это не ошибка, а особенность работы 1С с метаданными запросов. Однако избавление от него значительно упрощает код, избавляя от необходимости вручную присваивать Строка.Значение = 0 при каждом добавлении данных.Проанализировав эти методы, мы видим, что наиболее производительным и архитектурно верным является первый способ. Он позволяет разделить логику получения данных (запрос) и логику хранения данных (типизированная ТЗ), что делает ваш код более надежным и предсказуемым.