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