При интеграции 1С с внешними информационными системами (CRM, ERP, сайтами или мобильными приложениями) разработчики постоянно сталкиваются с необходимостью сопоставления объектов. Внешние системы, как правило, оперируют уникальными текстовыми идентификаторами формата UUID (GUID). Внутри платформы 1С каждый ссылочный объект также обладает своим уникальным идентификатором, но напрямую использовать его в запросах раньше было нетривиальной задачей.
В этой статье мы подробно разберем, как получить значение справочника по его GUID внутри запроса, проанализируем ограничения платформы, рассмотрим внутреннюю кухню работы СУБД и изучим наиболее эффективные и быстрые алгоритмы сопоставления больших массивов данных.
Прежде чем перейти к практическим решениям, совершим небольшой экскурс в историю развития механизмов платформы. Это поможет понять, почему старые методы интеграции до сих пор встречаются в конфигурациях и почему некоторые интуитивно понятные конструкции вызывают ошибки выполнения запроса.
Начиная с версии платформы 8.3.22 в язык запросов и язык выражений СКД была добавлена долгожданная функция УНИКАЛЬНЫЙИДЕНТИФИКАТОР(). Она позволяет получить UUID ссылки непосредственно внутри запроса встроенными средствами.
Однако при попытке использовать этот функционал для связи с внешними текстовыми данными разработчики часто наступают на «грабли» типизации. Рассмотрим типичную ошибку при попытке связать временную таблицу внешних строковых данных с реальным справочником:
// ВНИМАНИЕ: Этот код вызовет ошибку выполнения!
Запрос.Текст =
"ВЫБРАТЬ
| ВТ_ВнешниеДанные.ИдентификаторСтрокой КАК ИдентификаторСтрокой,
| Номенклатура.Ссылка КАК Ссылка
|ИЗ
| ВТ_ВнешниеДанные КАК ВТ_ВнешниеДанные
| ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Номенклатура
| ПО (ВТ_ВнешниеДанные.ИдентификаторСтрокой = ВЫРАЗИТЬ(УНИКАЛЬНЫЙИДЕНТИФИКАТОР(Номенклатура.Ссылка) КАК СТРОКА(36)))";
Выясним причину неудачи. Тип данных УникальныйИдентификатор в платформе 1С является самостоятельным примитивным типом. Платформа не поддерживает явное приведение типов (кастинг) между типом УникальныйИдентификатор и строковыми типами внутри СУБД с помощью конструкции ВЫРАЗИТЬ. Для решения этой проблемы можно использовать методику сравнения строки с представлением ссылки в запросе.
Попытка обойти это через функцию ПРЕДСТАВЛЕНИЕ(УНИКАЛЬНЫЙИДЕНТИФИКАТОР(Номенклатура.Ссылка)) также обречена на провал. Виртуальное поле представления формируется платформой 1С на этапе постобработки выборки. Его категорически запрещено использовать в секциях ПО (для соединений таблиц), ГДЕ (для отборов) или СГРУППИРОВАТЬ ПО.
Давайте разберем три рабочих и эффективных сценария решения нашей задачи в зависимости от контекста.
Если перед нами стоит задача найти конкретный элемент справочника в запросе, зная его уникальный идентификатор, мы можем использовать встроенную функцию УНИКАЛЬНЫЙИДЕНТИФИКАТОР напрямую в секции отбора.
Для этого в параметры запроса необходимо передать не строковое представление, а объект типа УникальныйИдентификатор. Давайте посмотрим на пример кода:
// Создаем объект УникальныйИдентификатор из входящей строки
ИскомыйGUID = Новый УникальныйИдентификатор("5d0f6261-754d-11ed-80e9-00155d012402");
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка,
| Номенклатура.Код КАК Код,
| Номенклатура.Наименование КАК Наименование
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| УНИКАЛЬНЫЙИДЕНТИФИКАТОР(Номенклатура.Ссылка) = &ПараметрGUID";
Запрос.УстановитьПараметр("ПараметрGUID", ИскомыйGUID);
РезультатЗапроса = Запрос.Выполнить();
Оценка производительности: Многие разработчики опасаются, что конструкция УНИКАЛЬНЫЙИДЕНТИФИКАТОР(Ссылка) приведет к сканированию всей таблицы справочника (Table Scan) и будет вычисляться для каждого элемента базы. На самом деле на уровне СУБД поле ссылки (ID) физически является уникальным идентификатором. СУБД выполняет быструю операцию Cluster Index Seek (поиск по кластерному индексу), поэтому такой запрос отрабатывает мгновенно (за 0 миллисекунд), независимо от объема справочника.
Рассмотрим наиболее частый рабочий сценарий: внешняя система прислала нам массив данных (например, 5 000 строк номенклатуры с их строковыми GUID). Нам нужно сопоставить эту таблицу со справочником в 1С и получить ссылки на существующие элементы. Для подобного сопоставления без написания кода можно использовать инструмент для массового поиска объектов 1С по уникальным идентификаторам.
Передавать таблицу значений со строковыми GUID в запрос и пытаться сопоставить их внутри СУБД — неэффективно. Вместо этого мы применим классический паттерн предварительной генерации ссылок в оперативной памяти.
Метод менеджера справочника ПолучитьСсылку() выполняет математическое преобразование строкового представления GUID в объект типа СправочникСсылка на сервере приложений 1С без обращения к базе данных. Это позволяет мгновенно подготовить типизированную таблицу значений для запроса.
Проанализируем этот алгоритм по шагам:
ПолучитьСсылку(Новый УникальныйИдентификатор(Строка.GUID)).Рассмотрим практическую реализацию этого метода:
// Предположим, у нас есть ТЗВнешниеДанные с колонкой "ID_Внешний" (строка)
// Шаг 1: Добавляем колонку для ссылки 1С
ТЗВнешниеДанные.Колонки.Добавить("НоменклатураСсылка", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
// Шаг 2: Заполняем ссылки в памяти (работает мгновенно)
Для Каждого СтрокаДанных Из ТЗВнешниеДанные Цикл
Попытка
// Метод ПолучитьСсылку() не делает запросов к базе данных!
СтрокаДанных.НоменклатураСсылка = Справочники.Номенклатура.ПолучитьСсылку(
Новый УникальныйИдентификатор(СтрокаДанных.ID_Внешний)
);
Исключение
// Обработка некорректного формата входящей строки GUID
СтрокаДанных.НоменклатураСсылка = Справочники.Номенклатура.ПустаяСсылка();
КонецПопытки;
КонецЦикла;
// Шаг 3: Передаем подготовленную таблицу в запрос
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Запрос.Текст =
"ВЫБРАТЬ
| ВТ_ВнешниеДанные.ID_Внешний КАК ID_Внешний,
| ВТ_ВнешниеДанные.НоменклатураСсылка КАК НоменклатураСсылка,
| НЕ Номенклатура.Ссылка ЕСТЬ NULL КАК ОбъектСуществуетВБазе
|ПОМЕСТИТЬ ВТ_СопоставленныеДанные
|ИЗ
| &ТаблицаВнешнихДанных КАК ВТ_ВнешниеДанные
| ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Номенклатура
| ПО ВТ_ВнешниеДанные.НоменклатураСсылка = Номенклатура.Ссылка";
Запрос.УстановитьПараметр("ТаблицаВнешнихДанных", ТЗВнешниеДанные);
Результат = Запрос.Выполнить();
Важная деталь: Так как метод ПолучитьСсылку() генерирует ссылку «вслепую» на основе предоставленного GUID, в базе данных этого объекта физически может не существовать (так называемая «битая ссылка»). Чтобы определить, существует ли данный объект в СУБД, мы выполняем левое соединение со справочником и проверяем реальное поле ссылки на НЕ NULL (поле ОбъектСуществуетВБазе в примере выше).
Этот подход признан эталонным в сообществе разработчиков 1С, поскольку сопоставление 5 000 и более строк занимает всего несколько миллисекунд.
Если архитектура интеграции предполагает постоянные сложные запросы со стороны внешних систем по текстовым полям, а предобработку данных на стороне 1С делать по каким-то причинам невозможно, мы можем прибегнуть к альтернативному варианту.
Рассмотрим вариант хранения строкового представления уникального идентификатора непосредственно в реквизите справочника:
Номенклатура новый реквизит (например, ИдентификаторДляИнтеграции) с типом Строка(36).ПередЗаписью прописываем автоматическое заполнение этого поля:
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
Если ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
// Заполняем текстовый реквизит строковым представлением GUID
ИдентификаторДляИнтеграции = Строка(Ссылка.УникальныйИдентификатор());
КонецПроцедуры
После этого мы можем легко связывать любые внешние текстовые таблицы с нашей базой напрямую в запросе без использования тяжелых функций конвертации:
Запрос.Текст =
"ВЫБРАТЬ
| ВТ_ВнешниеДанные.ID_Внешний КАК ID_Внешний,
| Номенклатура.Ссылка КАК Ссылка
|ИЗ
| ВТ_ВнешниеДанные КАК ВТ_ВнешниеДанные
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Номенклатура
| ПО ВТ_ВнешниеДанные.ID_Внешний = Номенклатура.ИдентификаторДляИнтеграции";
Преимущества: Простота написания запросов, высокая скорость соединения за счет использования индекса СУБД по текстовому полю.
Недостатки: Избыточность хранения данных (база данных увеличивается в размерах из-за дублирования GUID в строковом формате и затрат на индексы), необходимость обслуживания этого индекса и наполнения исторических данных.
Подводя итог, выберем оптимальную стратегию работы с уникальными идентификаторами в запросах 1С:
УНИКАЛЬНЫЙИДЕНТИФИКАТОР(Ссылка) = &ПараметрGUID, передавая в параметр объект типа УникальныйИдентификатор. Это работает мгновенно благодаря индексам СУБД.ПолучитьСсылку() с последующей передачей во временную таблицу запроса. Это самый быстрый и надежный способ интеграции, не нагружающий базу данных лишними операциями.УникальныйИдентификатор в Строка внутри текста запроса — платформа 1С не поддерживает такую конвертацию на уровне СУБД.