При работе с платформой 1С:Предприятие разработчики часто сталкиваются с необходимостью прямого обращения к таблицам СУБД (MS SQL Server, PostgreSQL) для построения сложных отчетов или интеграции с внешними системами. Основная сложность заключается в том, что привычный нам УникальныйИдентификатор (UUID), который мы видим в коде 1С, не совпадает по формату с тем, как он физически записан в поле _IDRRef в таблицах базы данных. В этой статье мы подробно разберем, почему так происходит, и как получить «тот самый» ID программными средствами.
Прежде всего, проанализируем ситуацию: в 1С УникальныйИдентификатор представлен в виде строки формата XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX. Однако в SQL-сервере этот же идентификатор хранится в поле _IDRRef как тип данных binary(16). Самое важное — порядок байтов в этих двух представлениях различается.
Почему 1С «перемешивает» байты? Это делается для оптимизации работы индексов в СУБД. Платформа переставляет группы байтов таким образом, чтобы новые записи при добавлении в таблицу попадали в конец индекса. Это значительно снижает фрагментацию индексов и повышает производительность при массовой записи данных.
Для того чтобы получить значение, которое можно использовать в прямом SQL-запросе, нам необходимо выполнить побайтовую перестановку. Разделим стандартный UUID на пять групп: {1}-{2}-{3}-{4}-{5}. Порядок байтов в 1С (UUID) и в SQL (_IDRRef) будет соотноситься следующим образом:
Если говорить точнее, то в 1С УникальныйИдентификатор версии 4 генерируется на основе времени, и именно временные составляющие перемещаются в начало для «последовательности» значений в SQL-индексе.
Самый быстрый способ увидеть «внутреннее» представление ссылки внутри кода 1С — это использование функции ЗначениеВСтрокуВнутр(). Разберем на примере:
// Получаем ссылку на элемент справочника
СсылкаНаЭлемент = Справочники.Номенклатура.НайтиПоНаименованию("Товар 1");
// Получаем внутреннее представление
ВнутренняяСтрока = ЗначениеВСтрокуВнутр(СсылкаНаЭлемент);
// Результат будет иметь вид: {"#",88261b80-3601-11e5-80c1-00155d013e01,{-1,00000000000000000000000000000000}}
В этой строке содержится 32-символьный шестнадцатеричный код. Это значение максимально близко к тому, что лежит в поле _IDRRef, но оно все равно может требовать перестановки блоков, если вы планируете использовать его в WHERE фильтрах SQL-запросов через внешние источники данных.
Если нам нужно программно подготовить строку для прямого запроса к БД, проанализируем следующий алгоритм на языке 1С. Мы возьмем УникальныйИдентификатор и переставим его части так, как это делает платформа при записи в SQL.
Функция ПолучитьIDДляSQL(Ссылка)
// Получаем UUID из ссылки
УИД = Строка(Ссылка.УникальныйИдентификатор());
// Убираем дефисы
Стр = СтрЗаменить(УИД, "-", "");
// Выполняем перестановку групп байтов (инверсия для SQL)
// Формат в SQL: Группа4 + Группа5 + Группа3 + Группа2 + Группа1
// (Это упрощенная схема, реальный порядок зависит от версии платформы)
Результат = Сред(Стр, 17, 4) + Сред(Стр, 21, 12) + Сред(Стр, 13, 4) + Сред(Стр, 9, 4) + Лев(Стр, 8);
Возврат "0x" + Результат;
КонецФункции
Важный момент: Данный метод возвращает строковое представление бинарного кода, которое SQL сервер воспримет как идентификатор при использовании префикса 0x.
Чтобы понять, в какой именно таблице искать поле _IDRRef, нам нужно сопоставить имя объекта в конфигураторе (например, Справочник.Контрагенты) с именем таблицы в СУБД (например, _Reference45). Для этого выясним структуру хранения базы данных.
Воспользуемся встроенным методом платформы:
// Получаем структуру хранения всех таблиц
СтруктураХранения = ПолучитьСтруктуруХраненияБазыДанных();
// Можно отфильтровать по конкретному объекту
МассивИмен = Новый Массив;
МассивИмен.Добавить("Справочник.Номенклатура");
СтруктураОбъекта = ПолучитьСтруктуруХраненияБазыДанных(МассивИмен);
Для Каждого СтрокаТаблицы Из СтруктураОбъекта Цикл
Сообщить("Имя таблицы в БД: " + СтрокаТаблицы.ИмяТаблицыХранения);
КонецЦикла;
Теперь, зная имя таблицы и имея алгоритм преобразования идентификатора, мы можем выполнить прямой запрос к СУБД:
-- Пример запроса в SQL Management Studio
SELECT * FROM _Reference45 WHERE _IDRRef = 0x80C100155D013E0111E5360188261B80
Мы выяснили, что получение ID элемента из БД напрямую — задача нетривиальная из-за специфической логики хранения данных платформой 1С. Подведем итоги наших шагов:
ЗначениеВСтрокуВнутр().ПолучитьСтруктуруХраненияБазыДанных(), так как имена таблиц могут меняться при реструктуризации (например, при удалении и добавлении объектов).Использование прямых запросов к базе данных — это мощный инструмент, но помните о безопасности и о том, что структура таблиц является закрытой информацией фирмы "1С", которая может измениться в будущих версиях платформы.