Артикул или номер договора хранится строкой: как сравнить его с числом в запросе
Содержание
Чтобы в запросе 1С сравнить текстовое поле с числом, приведите к строке не поле, а параметр: в коде превратите число в строку через Формат(Число, "ЧГ=0") и сравнивайте строку со строкой. Язык запросов не умеет превращать строку в число через ВЫРАЗИТЬ, поэтому все рабочие способы сводятся к трём: строковый параметр, перевод в число в коде и временная таблица, либо отдельный числовой реквизит в метаданных.
Типичная ситуация: в справочнике «Номенклатура» артикул хранится строкой, а менеджер просит отчёт «все артикулы от 1000 до 2000». Или у договоров есть текстовый реквизит «НомерДоговора», и нужно найти договор номер 125, а в базе он записан как 0125. Запрос, написанный «в лоб», либо ничего не находит, либо сортирует 10 раньше 9.
Почему строка с цифрами в запросе 1С не равна числу
Для платформы "125" и 125 — значения разных типов. Строку она сравнивает посимвольно, слева направо, как слова в словаре. Число сравнивает по величине.
Отсюда три типовых сбоя:
- 🔹 Пустой результат. Условие
ГДЕ Т.Артикул = &Артикул, куда передано число, не находит строку"125". В зависимости от выражения запрос может и вовсе завершиться ошибкой о несовместимых типах. - 🔹 Неверный диапазон.
Т.Артикул МЕЖДУ "1000" И "2000"захватит"15","1999999"и"1A": посимвольно они лежат между границами. - 🔹 Дубли.
"0125","125"и"125 "с пробелом в конце — три разных значения. В группировке получатся три строки вместо одной.
Источник проблемы почти всегда в метаданных. Разработчик когда-то выбрал тип «Строка», потому что в артикуле иногда бывают буквы или дефис. Или номер договора приходит от контрагента в произвольном виде. Тип поля переделывать долго, поэтому задачу приходится решать в запросе.
Номер документа и код справочника тоже бывают строковыми. Это задаётся свойствами объекта в конфигураторе: «Тип номера» у документа и «Тип кода» у справочника. В типовых конфигурациях они почти всегда строковые, с префиксами.
Как привести текст к числу в запросе 1С и чем заменить ВЫРАЗИТЬ
Первое, что пробует программист, — ВЫРАЗИТЬ(Т.Артикул КАК ЧИСЛО(10, 0)). Не работает
: приведение строки к числу язык запросов не поддерживает. Обратное направление, число в строку, тоже недоступно.
Что ВЫРАЗИТЬ умеет на самом деле:
| Приведение | Пример | Зачем нужно |
|---|---|---|
| Строка неограниченной длины → строка фиксированной длины | ВЫРАЗИТЬ(Т.Комментарий КАК СТРОКА(100)) | Сравнивать, группировать, упорядочивать по длинному тексту |
| Число → число другой разрядности | ВЫРАЗИТЬ(Т.Сумма КАК ЧИСЛО(15, 2)) | Округлить, задать точность результата |
| Составной тип → один из его типов | ВЫРАЗИТЬ(Т.Регистратор КАК Документ.РеализацияТоваровУслуг) | Обратиться к реквизитам конкретного типа |
Поэтому рабочие способы такие.
Способ 1. Строковый параметр вместо числового
Самый быстрый и безопасный. Число превращается в строку в коде, до выполнения запроса:
НомерИзФормы = 125;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Договоры.Ссылка,
| Договоры.НомерДоговора
|ИЗ
| Справочник.ДоговорыКонтрагентов КАК Договоры
|ГДЕ
| Договоры.НомерДоговора = &Номер";
Запрос.УстановитьПараметр("Номер", Формат(НомерИзФормы, "ЧГ=0"));
Без ЧГ=0 число 1250 превратится в строку с разделителем групп разрядов — и условие опять ничего не найдёт. Для склейки числа со строкой всегда используйте Формат с этим параметром.
Если в базе номера хранятся с ведущими нулями до фиксированной длины, добавьте их и в параметр: Формат(125, "ЧЦ=4; ЧВН=; ЧГ=0") вернёт "0125". Здесь ЧЦ — общее число знаков, ЧВН — выводить ведущие нули.
Способ 2. Отбор «только цифры» прямо в запросе
Оператор ПОДОБНО понимает диапазоны символов в квадратных скобках. Шаблон [^0-9] означает «любой символ, кроме цифры». Значит, строка без единой нецифры — числовая:
ВЫБРАТЬ
Номенклатура.Ссылка,
Номенклатура.Артикул
ИЗ
Справочник.Номенклатура КАК Номенклатура
ГДЕ
Номенклатура.Артикул <> ""
И НЕ Номенклатура.Артикул ПОДОБНО "%[^0-9]%"
Числа это поле от этого не становится. Зато дальше с результатом можно спокойно работать в коде, не боясь ошибки преобразования.
Способ 3. Перевод в коде и временная таблица
Когда нужен именно числовой диапазон или сумма по номерам, переводите значения в коде, а затем кладите их обратно в запрос временной таблицей. Схема в три шага:
- Запросом получить
РАЗЛИЧНЫЕзначения текстового поля. - В цикле отобрать числовые и перевести их в число.
- Передать таблицу «строка — число» параметром, поместить во временную таблицу и соединить с основной выборкой.
Функция ТолькоЦифры(Знач Стр)
Стр = СокрЛП(Стр);
Если Стр = "" Или СтрДлина(Стр) > 15 Тогда
Возврат Ложь;
КонецЕсли;
Для Н = 1 По СтрДлина(Стр) Цикл
Если СтрНайти("0123456789", Сред(Стр, Н, 1)) = 0 Тогда
Возврат Ложь;
КонецЕсли;
КонецЦикла;
Возврат Истина;
КонецФункции
Соответствие = Новый ТаблицаЗначений;
Соответствие.Колонки.Добавить("Артикул",
Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(25)));
Соответствие.Колонки.Добавить("АртикулЧислом",
Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 0)));
Выборка = ЗапросАртикулов.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Если ТолькоЦифры(Выборка.Артикул) Тогда
НоваяСтрока = Соответствие.Добавить();
НоваяСтрока.Артикул = Выборка.Артикул;
НоваяСтрока.АртикулЧислом = Число(СокрЛП(Выборка.Артикул));
КонецЕсли;
КонецЦикла;
Дальше основной запрос:
ВЫБРАТЬ
Т.Артикул,
Т.АртикулЧислом
ПОМЕСТИТЬ ВТ_Артикулы
ИЗ
&Соответствие КАК Т
ИНДЕКСИРОВАТЬ ПО
Т.Артикул
;
ВЫБРАТЬ
Номенклатура.Ссылка,
ВТ_Артикулы.АртикулЧислом
ИЗ
Справочник.Номенклатура КАК Номенклатура
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Артикулы КАК ВТ_Артикулы
ПО Номенклатура.Артикул = ВТ_Артикулы.Артикул
ГДЕ
ВТ_Артикулы.АртикулЧислом МЕЖДУ &От И &До
УПОРЯДОЧИТЬ ПО
ВТ_Артикулы.АртикулЧислом
Типизированные колонки обязательны. Без ОписаниеТипов платформа не сможет поместить таблицу значений во временную таблицу.
В конфигурациях на БСП (Бухгалтерия, ЗУП, УТ) есть готовая функция СтроковыеФункцииКлиентСервер.СтрокаВЧисло. Для нечисловой строки она возвращает Неопределено, а не падает с исключением.
Сортировка и группировка числовых строк
Строковая сортировка ставит "10" перед "9", потому что символ 1 меньше символа 9. Исправить это можно тремя путями.
Выровнять длину ведущими нулями
Если все значения одной длины — "0009", "0010", "0125", — строковый порядок совпадает с числовым. Тогда и МЕЖДУ "0100" И "0500" даёт правильный диапазон. Это лучший вариант для новых справочников: договоритесь о формате номера с самого начала.
Сортировать по длине, затем по значению
В платформе 8.3.20 и выше в языке запросов появились строковые функции, в том числе ДЛИНАСТРОКИ и СОКРЛП. Для строк из одних цифр без ведущих нулей такой порядок совпадает с числовым:
УПОРЯДОЧИТЬ ПО
ДЛИНАСТРОКИ(Номенклатура.Артикул),
Номенклатура.Артикул
Сначала идут все однозначные, затем двузначные и так далее. Для новых функций нужен не только свежий релиз платформы, но и режим совместимости конфигурации не ниже 8.3.20. Прямого приведения строки к числу среди этих функций нет: проверьте синтаксис-помощник своей версии.
Отсортировать уже в коде
Если результат выгружен в таблицу значений с числовой колонкой (способ 3), сортируйте её методом Сортировать("АртикулЧислом"). Для печатной формы на пару сотен строк это проще любых трюков в запросе.
Группировка
Чтобы "0125" и "125" попали в одну группу, их нужно привести к единому виду. Ведущие нули язык запросов отрезать не умеет, поэтому группируйте по числовой колонке из временной таблицы. Хвостовые пробелы на 8.3.20 и выше снимает СОКРЛП(Т.Артикул) прямо в СГРУППИРОВАТЬ ПО.
Грязные данные: пробелы, нули, буквы и пустые значения
На реальной базе в «числовом» поле оказывается что угодно. Перед любым отчётом посмотрите, что там лежит на самом деле.
Что встречается чаще всего:
- 🔸 Пробелы по краям — следы ручного ввода и загрузки из Excel.
- 🔸 Строка фиксированной длины. Если у реквизита в конфигураторе задана фиксированная длина, значения дополняются пробелами справа. Шаблон
%[^0-9]%тогда отбракует все значения: пробел не цифра. - 🔸 Префиксы и разделители:
АР-125,125/1,№125. - 🔸 Пустая строка. Под шаблон «нет нецифр» она подходит, поэтому её отсекают отдельным условием.
- 🔸 Слишком длинное число. Строка из 20 цифр не влезет в
Число(15, 0). В функции выше это проверяется по длине.
Проверить базу можно одним запросом-диагностикой:
ВЫБРАТЬ
Номенклатура.Артикул,
КОЛИЧЕСТВО(*) КАК Сколько
ИЗ
Справочник.Номенклатура КАК Номенклатура
ГДЕ
Номенклатура.Артикул ПОДОБНО "%[^0-9]%"
СГРУППИРОВАТЬ ПО
Номенклатура.Артикул
Он покажет все значения, в которых есть хоть один нецифровой символ. Если таких десятки, проще почистить данные, чем усложнять каждый запрос.
Ошибка «Преобразование значения к типу Число не может быть выполнено»
Это исключение кода, а не запроса. Его выбрасывает функция Число(), когда получает строку с буквой или пустую строку. Решение — проверять строку до преобразования, как в функции ТолькоЦифры, или заключить вызов в Попытка … Исключение. Проверка дешевле: исключения в цикле на десятках тысяч строк заметно замедляют обработку.
Массовую правку артикулов или номеров делайте только на копии базы и после согласования. По этим полям могут работать обмены с сайтом, ТСД и поставщиками: изменённое значение перестанет совпадать на другой стороне.
Производительность запросов с текстом как числом
Сравнение строки с числом редко медленное само по себе. Медленными его делают обёртки вокруг поля.
| Условие | Индекс по полю | Комментарий |
|---|---|---|
Т.Артикул = &Строка | используется | Лучший вариант |
Т.Артикул ПОДОБНО "125%" | обычно используется | Шаблон начинается с конкретных символов |
Т.Артикул ПОДОБНО "%[^0-9]%" | нет | Просмотр всей таблицы |
СОКРЛП(Т.Артикул) = &Строка | нет | Функция над полем скрывает его от индекса |
| Соединение с временной таблицей по строке | используется | Если поле проиндексировано |
Практические правила:
- ✅ Обрабатывайте параметр, а не поле.
Форматв коде стоит ноль, функция над полем вГДЕстоит просмотра таблицы. - ✅ В
ИНДЕКСИРОВАТЬ ПОвременной таблицы указывайте поле, по которому идёт соединение. - ✅ Сужайте выборку до перевода в число. Сначала отбор по виду номенклатуры, организации или периоду, потом всё остальное.
- ✅ У реквизита, по которому регулярно ищут, должно быть включено свойство «Индексировать» в конфигураторе.
Перевод в коде через временную таблицу быстрый, пока различных значений тысячи или десятки тысяч. На сотнях тысяч он упирается в цикл и передачу данных на сервер. Здесь пора менять метаданные.
Решение в метаданных: отдельный числовой реквизит
Если отчёт по числовому значению нужен постоянно, храните число рядом со строкой. Строковый артикул остаётся для людей и обменов, числовой — для запросов.
Как это устроено
- Добавить реквизит, например
АртикулЧислом, типЧисло(15, 0), свойство «Индексировать» включено. - В модуле объекта, в обработчике
ПередЗаписью, заполнять его: если строка числовая — числом, иначе нулём. - Однократной обработкой заполнить реквизит у существующих элементов.
- Переписать запросы: отбор и сортировку делать по новому полю.
Процедура ПередЗаписью(Отказ)
Если ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
Стр = СокрЛП(Артикул);
АртикулЧислом = ?(ТолькоЦифры(Стр), Число(Стр), 0);
КонецПроцедуры
Учтите проверку ОбменДанными.Загрузка: при загрузке через обмен обработчик выходит сразу, и реквизит не заполнится. Если данные приходят из обменов, заполняйте поле и там, либо уберите эту проверку осознанно.
Типовая конфигурация: только через расширение
В Бухгалтерии, ЗУП или УТ не снимайте конфигурацию с поддержки ради одного реквизита. Добавьте его в расширении: начиная с платформы 8.3.11 к заимствованным объектам можно добавлять собственные реквизиты. Код заполнения пишется в модуле объекта расширения через &После("ПередЗаписью").
Сменить тип поля целиком
Если в поле никогда не было букв, реквизит можно перевести на тип «Число». При обновлении структуры базы платформа попытается сконвертировать существующие значения. Перед этим обязательно:
- 🔹 выполнить запрос-диагностику из раздела о грязных данных;
- 🔹 сделать выгрузку
.dtи проверить результат на копии; - 🔹 найти все места, где поле используется как строка: обмены, печатные формы, поиск в формах.
Обновление структуры требует монопольного доступа к базе. Все пользователи будут отключены, поэтому делайте это вне рабочего времени.