В практике разработки на платформе 1С:Предприятие часто возникает классическая коллизия типов данных. Например, в одном документе штрихкод хранится в виде Числа (с разделителями групп разрядов, например, 200 000 576 241), а в регистре сведений тот же штрихкод записан как Строка без пробелов (200000576241). При попытке связать эти таблицы в запросе напрямую через ПО разработчик сталкивается с невозможностью сопоставить разные типы данных.
В этой статье мы подробно разберем, как решить эту задачу различными методами: от современных возможностей платформы до обходных путей в СКД и правильного архитектурного проектирования базы данных.
Если ваша информационная база работает на платформе версии 8.3.20.1549 или более поздней, вам крупно повезло. Фирма «1С» добавила в язык запросов полноценную поддержку функций работы со строками. Теперь мы можем преобразовывать число в строку и удалять пробелы непосредственно внутри запроса.
Рассмотрим важный нюанс: при автоматическом приведении числа к строке платформа 1С использует локальные настройки ИБ и по умолчанию разделяет триады чисел неразрывным пробелом (символ с кодом ASCII 160 или Unicode U+00A0). Обычная замена стандартного пробела (код 32) функцией СтрЗаменить здесь не сработает. Нам нужно передать в качестве параметра именно неразрывный пробел.
Проанализируем пример запроса, решающего эту задачу:
ВЫБРАТЬ
СтрЗаменить(Строка(ДокументТаблица.ШтрихкодДок), &НеразрывныйПробел, "") КАК ШтрихкодСтрокой,
ДокументТаблица.Ссылка КАК ДокументСсылка
ПОМЕСТИТЬ ВтДокументыСоШтрихкодом
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК ДокументТаблица
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВтДокументы.ДокументСсылка КАК ДокументСсылка,
РегистрШтрихкоды.Штрихкод КАК ШтрихкодРегистра
ИЗ
ВтДокументыСоШтрихкодом КАК ВтДокументы
ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ШтрихкодыНоменклатуры КАК РегистрШтрихкоды
ПО ВтДокументы.ШтрихкодСтрокой = РегистрШтрихкоды.Штрихкод
В качестве параметра &НеразрывныйПробел в коде вызова запроса необходимо передать значение Символы.НПП:
Запрос = Новый Запрос;
Запрос.Текст = "..."; // Текст запроса выше
Запрос.УстановитьПараметр("НеразрывныйПробел", Символы.НПП);
Результат = Запрос.Выполнить();
Этот способ является наиболее изящным и быстрым, поскольку вся обработка происходит непосредственно на уровне СУБД силами встроенного транслятора SQL.
Если вы работаете на более ранних версиях платформы (до 8.3.20), то в обычном запросе преобразовать число в строку штатными методами невозможно (функция ВЫРАЗИТЬ не поддерживает приведение чисел к строкам). Однако мы можем использовать механизм Системы компоновки данных (СКД).
СКД обладает уникальной возможностью связывать наборы данных не только по физическим полям таблиц, но и по вычисляемым выражениям. Разберем этот процесс по шагам:
НаборДанныхДокумент), который выбирает данные документа с числовым полем ШтрихкодДок.НаборДанныхРегистр), выгружающий строковые штрихкоды из регистра сведений.Формат(ШтрихкодДок, "ЧГ=0")ШтрихкодФункция Формат(..., "ЧГ=0") полностью убирает группировку разрядов (пробелы) из числа и возвращает чистую строку, пригодную для сопоставления.
Важное предупреждение о производительности: помните, что соединение наборов данных по вычисляемому выражению происходит на уровне сервера приложений 1С (постобработка), а не на СУБД. Платформа сначала получит все записи из БД для обоих наборов, а затем начнет сопоставлять их в оперативной памяти. На таблицах объемом более 10-20 тысяч строк этот метод может вызвать сильные задержки в работе отчета.
Как справедливо заметили участники обсуждения на форуме, хранение штрихкода (и любых других аналогичных кодов, номеров, артикулов) в формате Числа — это серьезная архитектурная ошибка. Штрихкод не является математической величиной: вы не будете складывать штрихкоды, вычитать их или делить. Штрихкод — это уникальный строковый идентификатор, который может начинаться с нулей (например, 00000123456), и при числовом хранении эти лидирующие нули будут безвозвратно утеряны.
Самым правильным решением будет приведение структуры метаданных к единому стандарту. Мы рекомендуем добавить в документ строковый реквизит (например, ШтрихкодСтрокой с типом Строка(13)) и настроить его автоматическое заполнение.
Разберем по шагам, как выполнить этот переход без потери старых данных:
Шаг 1. Добавьте в документ новый реквизит ШтрихкодСтрокой (Длина 13, Допустимые символы — Любые, Индексировать — Да).
Шаг 2. В модуле объекта документа в процедуре ПередЗаписью пропишите автоматическое заполнение нового поля при изменении старого числового значения:
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
Если ЗначениеЗаполнено(ШтрихкодДок) Тогда
// Преобразуем число в строку без группировки разрядов
ШтрихкодСтрокой = Формат(ШтрихкодДок, "ЧГ=0");
Иначе
ШтрихкодСтрокой = "";
КонецЕсли;
КонецПроцедуры
Шаг 3. Напишите и запустите разовую обработку для заполнения нового строкового реквизита в уже существующих в базе документах:
Выборка = Документы.ЗаказКлиента.Выбрать();
Пока Выборка.Следующий() Цикл
Если ЗначениеЗаполнено(Выборка.ШтрихкодДок) И Не ЗначениеЗаполнено(Выборка.ШтрихкодСтрокой) Тогда
ДокументОбъект = Выборка.ПолучитьОбъект();
ДокументОбъект.ШтрихкодСтрокой = Формат(Выборка.ШтрихкодДок, "ЧГ=0");
// Записываем без проведения и проверок для ускорения процесса
ДокументОбъект.Записать(РежимЗаписиДокумента.Запись);
КонецЕсли;
КонецЦикла;
После выполнения этих шагов вы сможете делать классическое, легкое и быстрое внутреннее соединение в любом запросе на уровне СУБД, используя проиндексированное строковое поле.
Для полноты картины рассмотрим сложный математический алгоритм, который использовался разработчиками до появления платформы 8.3.20. Если у вас нет прав на изменение метаданных (чтобы применить Метод 3) и вы не можете использовать СКД (Метод 2), преобразовать число в строку можно чистой математикой в запросе.
Идея состоит в том, чтобы разделить число на разряды (сотни, десятки, единицы) математическими операциями деления и вычитания остатков, округлить их с помощью ВЫРАЗИТЬ КАК ЧИСЛО, а затем сопоставить каждую цифру с ее строковым символом из константы "0123456789" с помощью функции ПОДСТРОКА().
Рассмотрим пример такого математического разбора для трехзначного числа:
ВЫБРАТЬ
ПОДСТРОКА("0123456789", ВЫРАЗИТЬ(ИсходнаяТаблица.Число / 100 - 0.5 КАК ЧИСЛО(10, 0)) + 1, 1) +
ПОДСТРОКА("0123456789", ВЫРАЗИТЬ((ИсходнаяТаблица.Число - ВЫРАЗИТЬ(ИсходнаяТаблица.Число / 100 - 0.5 КАК ЧИСЛО(10, 0)) * 100) / 10 - 0.5 КАК ЧИСЛО(10, 0)) + 1, 1) +
ПОДСТРОКА("0123456789", ИсходнаяТаблица.Число - ВЫРАЗИТЬ(ИсходнаяТаблица.Число / 10 - 0.5 КАК ЧИСЛО(10, 0)) * 10 + 1, 1) КАК ЧислоСтрокой
ИЗ
Справочник.Номенклатура КАК ИсходнаяТаблица
Этот запрос вычисляет каждую цифру отдельно, находит ее смещение в строке-шаблоне и склеивает результат обратно в строковый формат.
Важно: для 12-значного штрихкода этот запрос вырастет до гигантских размеров и станет практически нечитаемым, а нагрузка на процессор сервера СУБД при расчете триллионных значений возрастет многократно. Используйте данный метод исключительно в крайних случаях и для небольших диапазонов чисел.
Мы проанализировали четыре принципиально разных подхода к решению проблемы сопоставления числовых и строковых значений штрихкодов в 1С. Наилучшим выбором с точки зрения чистоты кода и быстродействия является Метод 3 (исправление архитектуры). Если же вы ограничены рамками существующей конфигурации, используйте современные функции платформы Строка() и СтрЗаменить() (Метод 1) или функционал связей наборов в СКД (Метод 2).