Почему нельзя использовать строковые измерения в регистрах 1С 7.7 при учете маркировки КИЗ и как решить проблему

Программист 1С v7.7 Торговля и дистрибуция
← На главную

При внедрении поштучного учета маркированных товаров (КИЗ, кодов маркировки DataMatrix систем «Честный Знак», МДЛП и др.) в устаревших, но все еще активно используемых конфигурациях на платформе 1С:Предприятие 7.7, разработчики часто совершают архитектурную ошибку. Они создают новый регистр остатков и добавляют в него измерение с типом Строка (например, длиной 100 или более символов) для хранения кодов маркировки.

В процессе эксплуатации такого решения программисты неизбежно сталкиваются с проблемами поиска, производительности СУБД и даже физической порчи данных. В этой статье мы подробно разберем, почему строковые измерения в регистрах платформы 1С 7.7 на MS SQL являются «миной замедленного действия», проанализируем технические причины сбоев и внедрим правильное архитектурное решение.

Суть проблемы: Сбой поиска из-за регистронезависимости MS SQL

Рассмотрим ситуацию. В регистре накопления создано строковое измерение для хранения КИЗ. При попытке оприходовать два схожих кода маркировки, которые отличаются только регистром одного символа (например, 0104660077310172210z00NnY и 0104660077310172210z00Nny), система начинает вести себя непредсказуемо. При попытке получить остатки или отфильтровать движения по второму коду, платформа всегда возвращает данные по первому или выдает нулевой итог.

Проанализируем код, который вызывает ошибку:


РегКИЗ = СоздатьОбъект("Регистр.КИЗ");
РегКИЗ.УстановитьФильтр("0104660077310172210z00Nny");
РегКИЗ.ВыбратьИтоги();
РегКИЗ.ПолучитьИтог(); // Результат всегда возвращает 0 или остаток по коду с "Y" на конце

Выясним причину такого поведения. По умолчанию при установке платформы 1С 7.7 на MS SQL Server база данных создается со стандартным сопоставлением (Collation) — обычно это Cyrillic_General_CI_AS. Параметр CI (Case Insensitive) означает, что СУБД при операциях сравнения строк и поиске по индексам игнорирует регистр символов. Для базы данных строки "NnY" и "Nny" абсолютно идентичны.

Когда платформа 1С 7.7 пытается выбрать итоги из таблицы RG* (таблица остатков регистров), SQL Server возвращает первую попавшуюся запись, удовлетворяющую условию без учета регистра. В результате логика закрытия регистров и списания остатков полностью ломается.

Опасность ручного изменения COLLATE в таблицах SQL

Некоторые администраторы баз данных и разработчики пытаются решить проблему «в лоб» — изменяют Collation конкретной колонки в таблице движений (RA*) и итогов (RG*) регистра через SQL Enterprise Manager на регистрозависимый (например, Cyrillic_General_CS_AS, где CS — Case Sensitive), после чего запускают тестирование и исправление (ТиИ) со сдвигом ТА и пересчетом итогов.

Делать это категорически запрещено по трем ключевым причинам:

  1. Конфликты сопоставления (Collation Conflict): Как только вы попытаетесь выполнить сложный прямой запрос к базе данных (например, используя популярную компоненту 1С++ или стандартные внутренние механизмы платформы) с операцией соединения таблиц (JOIN), MS SQL Server выдаст фатальную ошибку: Cannot resolve collation conflict for equal to operation. Это происходит из-за того, что сопоставление вашей измененной колонки не совпадает с сопоставлением временных таблиц или других справочников базы данных.
  2. Сброс настроек при реструктуризации: Платформа 1С 7.7 ничего не знает о ваших ручных изменениях в структуре таблиц на стороне SQL. При любом изменении метаданных в Конфигураторе, запуске полного ТиИ с реструктуризацией или при восстановлении базы из резервной копии (файла .zip) таблицы будут созданы заново со стандартным системным Collation. Все ручные настройки COLLATE будут безвозвратно утеряны, а данные «склеятся» заново, породив дубли и расхождения в итогах.
  3. Нарушение работы внутренних поисковых механизмов: Встроенный язык 1С 7.7 оптимизирован под регистронезависимый поиск. Появление чувствительных к регистру данных на уровне СУБД может нарушить работу стандартных выборок платформы.

Вторая проблема: Раздувание индексов и падение производительности

Даже если бы проблемы с регистрозависимостью не существовало, использование длинных строк в качестве измерений регистров — это грубая архитектурная ошибка. Давайте разберем физическую структуру хранения данных регистра в SQL.

Для каждой таблицы движений (RA*) и остатков (RG*) СУБД строит кластерные и некластерные индексы. В эти индексы в обязательном порядке входят все измерения регистра. Если одним из измерений является строка длиной 100 символов, мы получаем следующие критические проблемы:

Третья проблема: Спецсимволы (ASCII 29) и повреждение структуры данных

Коды маркировки формата GS1 DataMatrix содержат обязательные управляющие символы-разделители групп данных — символ GS (Group Separator), имеющий ASCII-код 29 (Симв(29)).

Штатная утилита тестирования и исправления 1С 7.7 («Проверка физической целостности») при сканировании строковых реквизитов считает любые управляющие ASCII-символы с кодами меньше 32 (за исключением перевода строки и табуляции) признаком повреждения структуры данных (мусором в таблице DBF/SQL). При первом же запуске ТиИ система очистит или обрежет ваши строки в местах нахождения символа Симв(29), сделав коды маркировки полностью нечитаемыми для контролирующих органов и кассового ПО.

Правильное архитектурное решение: Переход на справочники

Единственным надежным, стабильным и производительным решением является отказ от строковых измерений в регистрах и перенос КИЗ в подчиненный справочник.

Рассмотрим по шагам, как правильно реализовать эту архитектуру:

Шаг 1. Создание справочника КИЗ

Создадим новый справочник в конфигураторе. Назовем его КИЗ (или КодыМаркировки).

Шаг 2. Изменение измерения регистра

В регистре остатков ОстаткиКИЗ изменим тип измерения КИЗ со Строка 100 на Справочник.КИЗ. Теперь в физических таблицах SQL вместо длинных, чувствительных к регистру строк будет храниться компактный и надежный 9-символьный идентификатор ссылки.

Шаг 3. Реализация поиска и записи кодов маркировки

При получении КИЗ (из файла, ТСД или ЭДО) мы сначала ищем элемент в справочнике КИЗ. Если элемент не найден — создаем его. При поиске мы должны учитывать регистрозависимость на уровне кода 1С, чтобы избежать коллизий на SQL. Посмотрим на пример функции получения ссылки на КИЗ:


Функция ПолучитьСсылкаНаКИЗ(Знач ИсходныйКод) Экспорт
    Перем СпрКИЗ, Ссылка;
    
    // Очищаем код от управляющего символа GS перед сохранением,
    // либо заменяем его на безопасный печатный символ (например, "|")
    БезопасныйКод = СтрЗаменить(ИсходныйКод, Симв(29), "|");
    
    СпрКИЗ = СоздатьОбъект("Справочник.КИЗ");
    
    // Выполняем поиск по реквизиту. 
    // Поскольку SQL найдет код без учета регистра, мы должны
    // программно перепроверить соответствие регистра символов в 1С.
    СпрКИЗ.ВыбратьЭлементыПоРеквизиту("ПолныйКод", БезопасныйКод, 1, 0);
    Пока СпрКИЗ.ПолучитьЭлемент() = 1 Цикл
        // Сравниваем строки с учетом регистра на стороне клиента 1С
        Если СпрКИЗ.ПолныйКод = БезопасныйКод Тогда
            Возврат СпрКИЗ.ТекущийЭлемент();
        КонецЕсли;
    КонецЦикла;
    
    // Если точное совпадение не найдено, создаем новый элемент
    СпрКИЗ.Новый();
    СпрКИЗ.ПолныйКод = БезопасныйКод;
    СпрКИЗ.Записать();
    
    Возврат СпрКИЗ.ТекущийЭлемент();
КонецФункции

Шаг 4. Использование в документах и отчетах

Теперь при проведении документов прихода и расхода мы передаем в движение регистра не саму строку КИЗ, а полученную ссылку на элемент справочника. Проанализируем пример проведения прихода:


// ... код модуля проведения документа ...
Рег = Регистр.ОстаткиКИЗ;
Рег.НоваяДвижение();
Рег.КИЗ = ПолучитьСсылкаНаКИЗ(ТекущийКодМаркировки);
Рег.Количество = 1;
Рег.ДвижениеВыполнить();
// ...

Для извлечения реального текстового кода маркировки (например, для выгрузки в ОФД или Честный Знак) мы просто обращаемся к реквизиту справочника, заменяя безопасный разделитель обратно на управляющий символ:


СтрокаДляПечати = СтрЗаменить(ВыбранныйКИЗ.ПолныйКод, "|", Симв(29));

Альтернативный вариант: Использование кодирования Base64 или Hex

Если по каким-то причинам вы категорически не можете отказаться от строковых измерений (что крайне маловероятно при грамотном подходе к проектированию базы данных), существует альтернативный способ обойти ограничение регистронезависимости SQL — кодирование КИЗ в формат Base64 или Hex (шестнадцатеричный вид) перед записью в регистр.

Разберем этот механизм:

  1. При записи КИЗ в регистр строка преобразуется в шестнадцатеричный код (Hex) или Base64. В таком представлении символы верхнего и нижнего регистра кодируются разными наборами знаков, которые на уровне SQL гарантированно не будут пересекаться при поиске. Например, символ "Y" превратится в "59", а "y" — в "79".
  2. При чтении значения из регистра производится обратное декодирование.

Однако этот метод решает только проблему чувствительности к регистру, оставляя нерешенными проблемы гигантского размера индексов, медленной работы СУБД и раздувания физического размера файлов вашей базы данных. Поэтому хранение КИЗ через специализированный справочник остается единственным профессиональным стандартом разработки.

Резюме

Мы проанализировали архитектурные особенности хранения маркированных товаров в 1С 7.7. Перевод КИЗ в справочник позволяет:

← На главную