Как сравнить, отсортировать и посчитать строковое поле как число в запросе 1С

· 10 мин

Содержание

В языке запросов 1С 8.3 нет функции, которая превращает строку в число: конструкция ВЫРАЗИТЬ(Поле КАК ЧИСЛО) для строкового поля не работает , запрос завершится ошибкой. Рабочих путей три: собрать число из цифр прямо в запросе через вспомогательную таблицу, преобразовать значения во встроенном языке функцией Число() и вернуть их в запрос временной таблицей, или хранить число в отдельном числовом реквизите.

Выбор зависит от формата строки. Если в поле только цифры фиксированной длины, хватит запроса. Если там пробелы, запятые, буквы или мусор от загрузки из Excel, надёжнее обработать значения в коде.

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

ВЫРАЗИТЬ в запросе умеет немногое:

  • 🔹 привести значение составного типа к одному из его типов;
  • 🔹 уточнить тип ссылки (ВЫРАЗИТЬ(Регистратор КАК Документ.РеализацияТоваровУслуг));
  • 🔹 обрезать строку неограниченной длины до фиксированной (КАК СТРОКА(100));
  • 🔹 изменить разрядность числа (КАК ЧИСЛО(15, 2)).

Перевода «строка → число» и «число → строка» в этом списке нет. Запрос выполняется на стороне СУБД или файлового движка, и платформа не переносит туда разбор строк в числа.

Отсюда типичные симптомы, когда строку пытаются использовать как число:

  • ⚠️ сортировка по коду даёт 1, 10, 100, 2, 25, 3 — строки сравниваются посимвольно, а не по величине;
  • ⚠️ условие Номенклатура.Артикул > 150 не отбирает ничего или запрос падает на сравнении разных типов;
  • ⚠️ СУММА(Т.Значение) по строковому полю не считается: агрегатные функции суммы требуют число.

Посимвольное сравнение совпадает с числовым только при одинаковой длине строк. "007" < "012" — верно и для строк, и для чисел. "7" < "12" — для строк неверно, потому что "7" больше "1".

Способы преобразовать строку в число: что выбрать

СпособКогда подходитОграничения
Таблица цифр в запросестрока из одних цифр, длина до 3–4 знаковна длинных строках таблица растёт в 10 раз на каждый знак
Разбор по позициям через соединение с цифрамификсированный формат: "0045", "12.50"точка и длина должны стоять на одном месте
Число() в коде + временная таблицалюбой формат, нужна обработка ошибокдва запроса, данные проходят через сервер приложений
Числовой реквизитзначение нужно в отборах постояннодоработка конфигурации или расширение
Сравнение без преобразованиянужна только сортировка или диапазонработает лишь при выровненной длине

Для разовой выборки берите первый или третий вариант. Если отбор по «числовому» артикулу нужен в отчётах каждый день, правильное решение — числовой реквизит, остальное лечит симптом.

Способ 1. Собираем число из цифр прямо в запросе

Идея простая: язык запросов умеет склеивать строки оператором + и умножать числа. Значит, можно построить таблицу, где каждой строке "0"…"999" сопоставлено её число, и соединиться с ней по строке.

Шаг 1. Таблица цифр

ВЫБРАТЬ 0 КАК Ч, "0" КАК С ПОМЕСТИТЬ Цифры
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 1, "1"
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 2, "2"
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 3, "3"
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 4, "4"
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 5, "5"
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 6, "6"
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 7, "7"
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 8, "8"
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 9, "9"
;

Шаг 2. Таблица чисел без ведущих нулей

Перекрёстное соединение (таблицы через запятую в ИЗ) даёт все сочетания цифр. Условие Ц1.Ч > 0 убирает варианты вида "07", чтобы строка "7" нашла ровно одно число.

ВЫБРАТЬ Ц1.Ч КАК Число, Ц1.С КАК Строка
ПОМЕСТИТЬ Числа
ИЗ Цифры КАК Ц1

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

ВЫБРАТЬ Ц1.Ч * 10 + Ц2.Ч, Ц1.С + Ц2.С
ИЗ Цифры КАК Ц1, Цифры КАК Ц2
ГДЕ Ц1.Ч > 0

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

ВЫБРАТЬ Ц1.Ч * 100 + Ц2.Ч * 10 + Ц3.Ч, Ц1.С + Ц2.С + Ц3.С
ИЗ Цифры КАК Ц1, Цифры КАК Ц2, Цифры КАК Ц3
ГДЕ Ц1.Ч > 0

ИНДЕКСИРОВАТЬ ПО Строка
;

В таблице 1000 строк: 10 однозначных, 90 двузначных и 900 трёхзначных.

Шаг 3. Соединяем с данными

ВЫБРАТЬ
    Ном.Ссылка КАК Номенклатура,
    Ном.Артикул КАК Артикул,
    Числа.Число КАК АртикулЧислом
ИЗ
    Справочник.Номенклатура КАК Ном
        ЛЕВОЕ СОЕДИНЕНИЕ Числа КАК Числа
        ПО Ном.Артикул = Числа.Строка
ГДЕ
    Числа.Число МЕЖДУ 100 И 200
УПОРЯДОЧИТЬ ПО
    АртикулЧислом

Все три фрагмента — один пакетный запрос. Если артикул не число или длиннее трёх знаков, соединение даст NULL, и такие строки отсеются условием ГДЕ. Хотите видеть их в результате — уберите условие и проверяйте Числа.Число ЕСТЬ NULL.

Четвёртый знак превращает таблицу в 10 000 строк, пятый — в 100 000. Дальше этот способ теряет смысл: переходите к разбору по позициям или к коду.

Вариант для строк с ведущими нулями

Коды справочников часто хранятся как "000000123" с префиксом или без. Тогда таблица чисел не нужна: вырежьте значимую часть через ПОДСТРОКА и соберите число по позициям, соединяясь с таблицей Цифры для каждого знака:

ВЫБРАТЬ
    Т.Ссылка,
    Д1.Ч * 100 + Д2.Ч * 10 + Д3.Ч КАК Номер
ИЗ
    Справочник.Контрагенты КАК Т
        ЛЕВОЕ СОЕДИНЕНИЕ Цифры КАК Д1 ПО Д1.С = ПОДСТРОКА(Т.Код, 7, 1)
        ЛЕВОЕ СОЕДИНЕНИЕ Цифры КАК Д2 ПО Д2.С = ПОДСТРОКА(Т.Код, 8, 1)
        ЛЕВОЕ СОЕДИНЕНИЕ Цифры КАК Д3 ПО Д3.С = ПОДСТРОКА(Т.Код, 9, 1)

Позиции 7–9 здесь для кода длиной 9 знаков. Подставьте свои: посмотрите длину кода в свойствах справочника в конфигураторе.

Строка с дробной частью: «12.50» и «12,50»

Дробное значение собирается из двух частей: целой и дробной. Если формат постоянный (например, "0125.50": четыре знака, точка, два знака), используйте тот же разбор по позициям:

ВЫБРАТЬ
    Т.Ссылка,
    Д1.Ч * 1000 + Д2.Ч * 100 + Д3.Ч * 10 + Д4.Ч
        + (Д6.Ч * 10 + Д7.Ч) / 100 КАК Сумма
ИЗ
    ВТ_Загрузка КАК Т
        ЛЕВОЕ СОЕДИНЕНИЕ Цифры КАК Д1 ПО Д1.С = ПОДСТРОКА(Т.СуммаСтрокой, 1, 1)
        ЛЕВОЕ СОЕДИНЕНИЕ Цифры КАК Д2 ПО Д2.С = ПОДСТРОКА(Т.СуммаСтрокой, 2, 1)
        ЛЕВОЕ СОЕДИНЕНИЕ Цифры КАК Д3 ПО Д3.С = ПОДСТРОКА(Т.СуммаСтрокой, 3, 1)
        ЛЕВОЕ СОЕДИНЕНИЕ Цифры КАК Д4 ПО Д4.С = ПОДСТРОКА(Т.СуммаСтрокой, 4, 1)
        ЛЕВОЕ СОЕДИНЕНИЕ Цифры КАК Д6 ПО Д6.С = ПОДСТРОКА(Т.СуммаСтрокой, 6, 1)
        ЛЕВОЕ СОЕДИНЕНИЕ Цифры КАК Д7 ПО Д7.С = ПОДСТРОКА(Т.СуммаСтрокой, 7, 1)

Пятую позицию (разделитель) пропускаем, поэтому неважно, точка там или запятая. Проверка: "0125.50" → 0·1000 + 1·100 + 2·10 + 5 + (5·10 + 0)/100 = 125,5.

Чтобы не тащить лишние знаки после деления, оберните результат: ВЫРАЗИТЬ(... КАК ЧИСЛО(15, 2)).

Если длина целой части плавает ("5.5", "125.50", "1 250,00"), запросом это не разобрать надёжно. Такие данные переводите в коде.

Способ 2. Число() во встроенном языке и обратно в запрос

Самый гибкий путь: выбрать строки первым запросом, перевести в число на сервере, положить результат во временную таблицу и продолжить работу запросом.

Функция преобразования с обработкой ошибок

Число("abc") вызывает исключение «Преобразование значения к типу Число не может быть выполнено». Поэтому вызов всегда оборачивают в Попытка:

Функция СтрокаВЧисло(Знач Текст, ЗначениеПоУмолчанию = Неопределено)

    Текст = СтрЗаменить(Текст, " ", "");
    Текст = СтрЗаменить(Текст, Символы.НПП, "");
    Текст = СтрЗаменить(Текст, ",", ".");

    Если ПустаяСтрока(Текст) Тогда
        Возврат ЗначениеПоУмолчанию;
    КонецЕсли;

    Попытка
        Возврат Число(Текст);
    Исключение
        Возврат ЗначениеПоУмолчанию;
    КонецПопытки;

КонецФункции

Что здесь учтено:

  • 🧹 обычные пробелы и неразрывные (Символы.НПП) — их подставляет Формат() при разбивке на группы разрядов и Excel при выгрузке;
  • 🔁 запятая заменяется на точку, чтобы "12,50" и "12.50" давали одно число;
  • 🚫 мусор возвращает Неопределено, а не 0. Так ошибочную строку можно отличить от настоящего нуля.

Возврат результата во временную таблицу

Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Запрос.Текст =
"ВЫБРАТЬ
|    Ном.Ссылка КАК Ссылка,
|    Ном.Артикул КАК Артикул
|ИЗ
|    Справочник.Номенклатура КАК Ном";
Таблица = Запрос.Выполнить().Выгрузить();

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

Для Каждого Стр Из Таблица Цикл
    Значение = СтрокаВЧисло(Стр.Артикул);
    Если Значение <> Неопределено Тогда
        Стр.АртикулЧислом = Значение;
    КонецЕсли;
КонецЦикла;

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

Дальше ВТ_Артикулы доступна в любом запросе с тем же менеджером временных таблиц: соединяйте, отбирайте МЕЖДУ 100 И 200, суммируйте.

Колонка, которую передаёте в запрос параметром, должна иметь явный тип. Колонку, добавленную без ОписаниеТипов, платформа в запрос не примет. Колонки из Выгрузить() уже типизированы.

Строки, где перевод не удался, останутся с 0 в колонке АртикулЧислом. Если ноль может быть настоящим значением, добавьте колонку-флаг Ошибка типа Булево и заполняйте её в том же цикле.

В отчёте на СКД

В схеме компоновки данных можно добавить вычисляемое поле, выражение которого вызывает экспортную серверную функцию общего модуля, например МойМодуль.СтрокаВЧисло(Артикул). Отбор и сортировка по такому полю выполняются уже после запроса, в движке компоновки. На тысячах строк это нормально, на сотнях тысяч — заметно медленнее, чем временная таблица.

Сортировка и диапазон без преобразования

Иногда число не нужно вовсе — нужен лишь правильный порядок. Тогда хватит выравнивания длины.

  • 📏 Коды с ведущими нулями ("000123") уже сортируются правильно: длина одинаковая, строковый порядок совпадает с числовым.
  • 📐 Строки разной длины сортируйте сначала по длине, потом по значению: УПОРЯДОЧИТЬ ПО СТРДЛИНА(Т.Артикул), Т.Артикул. Тогда "9" встанет раньше "10". Функция СТРДЛИНА появилась в языке запросов в платформе 8.3.20 вместе с ЛЕВ, ПРАВ, СОКРЛП, СТРНАЙТИ, СТРЗАМЕНИТЬ.
  • 🎯 Диапазон для выровненных строк задаётся строками той же длины: Т.Код МЕЖДУ "000100" И "000200".

Проверить, что строка состоит только из цифр, можно оператором ПОДОБНО:

ГДЕ
    НЕ Т.Артикул ПОДОБНО "%[^0-9]%"
    И Т.Артикул <> ""

Шаблон [^0-9] означает «любой символ, кроме цифры». Условие пропускает строку, если в ней нет ни одного такого символа. Перед соединением с таблицей чисел это отсекает "12А", "1-5" и пустые значения.

Скорость: где теряется время

Любое выражение над полем в условии или соединении (ПОДСТРОКА(Т.Код, 7, 1), СТРДЛИНА(...)) не использует индекс по этому полю. СУБД перебирает таблицу целиком.

Что помогает:

  1. Сначала сузить выборку. Отберите документы по периоду и организации во временную таблицу, а преобразование делайте уже над ней.
  2. Индексировать временные таблицы. ИНДЕКСИРОВАТЬ ПО Строка в таблице чисел и ИНДЕКСИРОВАТЬ ПО АртикулЧислом в результате перевода ускоряют последующие соединения.
  3. Не строить таблицу больше нужного. Для трёхзначных значений хватит 1000 строк, не делайте 100 000 «на запас».
  4. Хранить число отдельно. Если отбор по числовому значению нужен регулярно, добавьте числовой реквизит (в расширении или конфигурации) и заполняйте его в обработчике ПередЗаписью объекта. Индекс по такому реквизиту снимает проблему полностью.

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

Ошибки, на которых спотыкаются

Диагностика: выберите симптом

Ещё одна ловушка — тип кода справочника. В свойствах справочника задаётся «Тип кода»: строка или число. Если код числовой, никакого преобразования не нужно: сравнивайте и сортируйте как есть. Проверьте это в конфигураторе до того, как писать разбор строк.

Частые вопросы

Можно ли в запросе сделать обратное — число превратить в строку?
Тоже нет: ВЫРАЗИТЬ не переводит число в строку. Используйте таблицу чисел из этой схемы (у неё есть колонка со строкой) или Формат() во встроенном языке.
Почему ВЫРАЗИТЬ(Поле КАК ЧИСЛО) иногда работает?
Когда поле составного типа и одним из его типов является число. Тогда ВЫРАЗИТЬ просто выбирает числовую часть, а строковые значения превращает в NULL.
Как отобрать документы по номеру больше заданного?
Номер документа — строка с префиксом. Если длина номера постоянная, сравнивайте строки той же длины с тем же префиксом. Если нет — вырежьте числовую часть через ПОДСТРОКА и соберите число по позициям.
Влияет ли тип СУБД на эти способы?
Таблица цифр, ПОДСТРОКА, ПОДОБНО и временные таблицы одинаково работают в файловой базе, MS SQL и PostgreSQL. Разница — в скорости на больших таблицах.
Что быстрее: таблица цифр или перевод в коде?
На десятках тысяч строк таблица цифр обычно быстрее: данные не уходят на сервер приложений и обратно. Перевод в коде выигрывает в надёжности, когда формат строк непредсказуем.