В процессе разработки прикладных решений на платформе «1С:Предприятие» перед программистами нередко встает задача хранения внешних GUID-идентификаторов в регистрах. Это может потребоваться для интеграции со сторонними сервисами, обмена данными между информационными базами или связывания внутренних объектов с внешними сущностями. Однако при попытке указать тип УникальныйИдентификатор в качестве измерения регистра накопления разработчик сталкивается с жестким ограничением платформы: этого типа просто нет в списке доступных.
В этой статье мы подробно разберем, почему платформа накладывает такое ограничение, как это связано с производительностью СУБД на физическом уровне, и рассмотрим лучшие практические варианты обхода этого запрета с примерами кода.
Прежде всего проанализируем поведение самой платформы. Если мы создаем Регистр сведений, платформа без проблем позволяет нам выбрать тип УникальныйИдентификатор для измерения. Но как только мы переходим к Регистру накопления (или регистру бухгалтерии/расчета), данный тип пропадает из списка доступных типов данных для измерений.
Для понимания причин такого поведения необходимо заглянуть под капот платформы и СУБД и оценить разницу в назначении этих объектов метаданных.
Основная задача любого регистра накопления — быстрое получение остатков и оборотов в разрезе его измерений. Для обеспечения высокой скорости выборки СУБД не рассчитывает остатки «на лету» по всей таблице движений. Вместо этого платформа 1С создает и поддерживает специальные таблицы итогов (агрегированные данные), которые хранят остатки на начало каждого месяца в разрезе измерений.
Если мы добавим в измерения регистра накопления уникальный идентификатор (GUID), который по своей сути является абсолютно уникальным для каждой записи, мы нарушим базовый принцип агрегации:
Именно поэтому разработчики платформы превентивно заблокировали возможность использовать УникальныйИдентификатор в измерениях регистров накопления.
Рассмотрим, как данные типов СправочникСсылка и УникальныйИдентификатор хранятся на уровне СУБД (например, MS SQL Server или PostgreSQL):
binary(16) (ровно 16 байт).nvarchar(36). В кодировке UTF-16, которую использует платформа 1С для строковых данных, такая строка физически занимает 72 байта.Разница в 4.5 раза в объеме хранимых данных оказывает сильное негативное влияние на индексы СУБД. Индекс по строковому измерению занимает существенно больше места в оперативной памяти и на диске. Кроме того, операции сравнения бинарных данных на уровне процессора выполняются значительно быстрее, чем посимвольное сравнение строк с учетом языковых правил (Collation).
При записи случайных значений GUID (особенно если они генерируются внешней системой не по алгоритму sequential/упорядоченного GUID) в индексируемое поле, СУБД сталкивается с проблемой сильной фрагментации индексов.
Когда новые данные вставляются в середину существующего индекса, СУБД вынуждена выполнять операцию разбиения страниц (Page Split). Это приводит к тому, что файлы базы данных фрагментируются, производительность дисковой подсистемы падает, а время выполнения запросов увеличивается.
Платформа 1С частично решает эту проблему для стандартных ссылок с помощью механизма побайтовой перестановки (Byte Swap) при записи GUID в таблицы SQL, оптимизируя их под кластерный индекс. При использовании обычной строки в качестве измерения эта оптимизация полностью теряется.
Если бизнес-логика вашей задачи все же требует ведения учета в регистре накопления в разрезе уникальных идентификаторов, у нас есть несколько путей решения. Рассмотрим их по шагам, взвесив все плюсы и минусы.
Это самый оптимальный и архитектурно верный способ («золотой стандарт» разработки в 1С). Вместо хранения строки или сырого GUID мы создаем специальный вспомогательный справочник-ключ.
Пошаговый алгоритм реализации:
КлючиСвязиВнешнихСистем.Код и Наименование (задаем их длину равной 0).СправочникСсылка.КлючиСвязиВнешнихСистем.Рассмотрим пример кода, который позволяет получить ссылку на элемент технического справочника по имеющемуся GUID без лишних запросов к базе данных:
// Функция возвращает ссылку на технический справочник по строковому представлению GUID.
// Если элемента нет в базе данных, он будет создан с указанным UUID.
Функция ПолучитьСсылкаНаКлючСвязи(СтрокаGUID) Экспорт
УИД = Новый УникальныйИдентификатор(СтрокаGUID);
СсылкаНаЭлемент = Справочники.КлючиСвязиВнешнихСистем.ПолучитьСсылку(УИД);
// Проверим, существует ли физически элемент в базе данных
Если СсылкаНаЭлемент.ПолучитьОбъект() = Неопределено Тогда
// Создаем новый элемент с предопределенным уникальным идентификатором
НовыйОбъект = Справочники.КлючиСвязиВнешнихСистем.СоздатьЭлемент();
НовыйОбъект.УстановитьСсылкуНового(СсылкаНаЭлемент);
НовыйОбъект.Записать();
КонецЕсли;
Возврат СсылкаНаЭлемент;
КонецФункции
Преимущества этого метода:
binary(16).Этот способ часто применяется в типовых конфигурациях 1С (например, в регистре СоответствиеОбъектовИнформационныхБаз). Мы объявляем измерение регистра с типом Строка, длина 36, фиксированная.
При записи данных мы преобразуем уникальный идентификатор в строку формата XML:
// Пример записи движения в регистр накопления с измерением типа Строка(36)
Движение = Движения.МойРегистрНакопления.Добавить();
Движение.Период = ТекущаяДата();
// Преобразуем объект УникальныйИдентификатор в строку фиксированной длины
Движение.ИдентификаторСтрокой = XMLСтрока(ДокументОбъект.УникальныйИдентификатор());
Движение.Сумма = 100;
Недостатки этого метода:
Использовать данный вариант стоит только в том случае, если объем записей в регистре заведомо невелик (до нескольких десятков тысяч записей), либо если регистр является регистром сведений, а не накопления.
Долгое время разработчики были вынуждены дублировать GUID в виде строк в измерениях регистров только ради того, чтобы иметь возможность быстро получать их текстовое представление в тяжелых запросах при интеграции (поскольку обращение через точку Ссылка.УникальныйИдентификатор в запросах выдавало ошибку).
Начиная с версии платформы 1С:Предприятие 8.3.22, эта проблема полностью решена. В язык запросов добавлена встроенная функция УникальныйИдентификатор(). Теперь вы можете получать строковый UUID любого ссылочного объекта напрямую из базы данных:
ВЫБРАТЬ
Номенклатура.Ссылка КАК Товар,
УникальныйИдентификатор(Номенклатура.Ссылка) КАК СтроковыйGUID
ИЗ
Справочник.Номенклатура КАК Номенклатура
Благодаря этому новшеству необходимость хранить строковые представления GUID в базах данных ради выгрузок во внешние системы практически сошла на нет.
Проанализировав особенности работы платформы «1С:Предприятие» с базами данных, мы пришли к следующим выводам:
УникальныйИдентификатор в измерениях регистров накопления обусловлен заботой о производительности механизма итогов и защитой от избыточного роста индексов СУБД.