В процессе разработки на платформе 1С:Предприятие 8 часто возникает задача формирования сложных строк прямо в запросе. Типичный пример — создание уникального ключа или читаемого представления, состоящего из типа документа, его номера и даты. Однако, когда мы работаем с составными типами данных, содержащими десятки различных видов документов, стандартная конструкция ВЫБОР КОГДА ... ТОГДА превращается в огромный, трудночитаемый и тяжело поддерживаемый блок кода.
Давайте разберем, почему стандартные функции преобразования типов не работают в запросах так, как нам хотелось бы, и проанализируем альтернативные способы решения этой задачи, которые позволят сделать ваш код более элегантным и производительным.
Первое, что приходит в голову начинающему разработчику — использовать функцию ПРЕДСТАВЛЕНИЕ(ДокументСсылка). Казалось бы, это логично, ведь функция возвращает строку. Однако здесь кроется главная архитектурная особенность платформы 1С. Разберем ситуацию подробнее.
Функция ПРЕДСТАВЛЕНИЕ() является "виртуальной". Она обрабатывается не на стороне SQL-сервера, а на стороне сервера приложений 1С уже после того, как данные были получены из базы. Именно поэтому попытка сложить результат этой функции с другой строкой или применить к ней ВЫРАЗИТЬ(... КАК СТРОКА) вызовет ошибку синтаксиса. Для SQL-сервера поле типа "Ссылка" — это набор двоичных данных (GUID), и он "не знает", какое текстовое название соответствует этому объекту в метаданных 1С. Для решения схожих задач фильтрации и сопоставления ссылочных полей со строками подойдёт способ сравнения строки с представлением ссылки в запросе.
Рассмотрим по шагам, какие у нас есть обходные пути для получения типа документа в виде строки без ручного написания сотен условий КОГДА.
В современных конфигурациях, построенных на базе БСП (Библиотека стандартных подсистем), существует системный справочник ИдентификаторыОбъектовМетаданных. В нем платформа хранит информацию обо всех объектах конфигурации, включая их имена и синонимы.
Проанализируем этот подход. Мы можем соединить основную таблицу с этим справочником, но есть одна сложность: в самой ссылке документа нет прямого поля, указывающего на идентификатор его типа в этом справочнике. Однако мы можем использовать временную таблицу. Разберем алгоритм:
ТИПЗНАЧЕНИЯ(ОсновнаяТаблица.Ссылка) = ТИПЗНАЧЕНИЯ(ВТ_Типы.ПустаяСсылка).Посмотрим на пример кода, реализующий этот метод:
// Подготовим таблицу типов
ТаблицаТипов = Новый ТаблицаЗначений;
ТаблицаТипов.Колонки.Добавить("ПустаяСсылка", Новый ОписаниеТипов("ДокументСсылка.ПриходныйКассовыйОрдер", "ДокументСсылка.РеализацияТоваровУслуг")); // Добавьте нужные типы
ТаблицаТипов.Колонки.Добавить("ИмяТипа", Новый ОписаниеТипов("Строка"));
// Заполним её (можно использовать цикл по Метаданные.Документы)
Для Каждого ДокМетаданные Из Метаданные.Документы Цикл
НоваяСтрока = ТаблицаТипов.Добавить();
НоваяСтрока.ПустаяСсылка = Документы[ДокМетаданные.Имя].ПустаяСсылка();
НоваяСтрока.ИмяТипа = ДокМетаданные.Синоним;
КонецЦикла;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ТТипов.ИмяТипа КАК ИмяТипа,
| Т.Ссылка КАК Ссылка
|ПОМЕСТИТЬ втТипы
|ИЗ
| &ТаблицаТипов КАК ТТипов
|;
|ВЫБРАТЬ
| втТипы.ИмяТипа + "" № "" + ОсновнаяТаблица.Номер КАК ПолноеНаименование
|ИЗ
| Документ.РеестрДокументов КАК ОсновнаяТаблица
|ВНУТРЕННЕЕ СОЕДИНЕНИЕ втТипы КАК втТипы
| ПО ТИПЗНАЧЕНИЯ(ОсновнаяТаблица.Ссылка) = ТИПЗНАЧЕНИЯ(втТипы.ПустаяСсылка)";
Запрос.УстановитьПараметр("ТаблицаТипов", ТаблицаТипов);
Если вы все же хотите использовать конструкцию ВЫБОР КОГДА, но типов слишком много, чтобы писать их вручную, лучшим решением будет программная сборка текста запроса. Это избавит вас от ошибок и сделает код адаптивным к изменениям в метаданных.
Выясним, как это сделать правильно. Мы пройдемся циклом по коллекции метаданных и сконструируем строку ВЫБОР автоматически.
ТекстВыбора = "ВЫБОР ";
Для Каждого Док Из Метаданные.Документы Цикл
ТекстВыбора = ТекстВыбора + "
| КОГДА Т.Документ ССЫЛКА Документ." + Док.Имя + " ТОГДА """ + Док.Синоним + """";
КонецЦикла;
ТекстВыбора = ТекстВыбора + " ИНАЧЕ ""Неизвестный тип"" КОНЕЦ";
ТекстЗапроса = "ВЫБРАТЬ " + ТекстВыбора + " + ""_"" + Т.Номер КАК ПолеНужное ИЗ ...";
Этот метод хорош тем, что итоговый SQL-запрос будет содержать только строковые константы, что работает очень быстро. Однако помните, что при огромном количестве типов (сотни) сам текст запроса может стать избыточно большим.
Проанализируем ситуацию с точки зрения производительности. Использование ТИПЗНАЧЕНИЯ в соединениях или длинных ВЫБОР КОГДА заставляет SQL-сервер выполнять множество LEFT JOIN к таблицам всех участвующих документов. Если ваша цель — просто вывести информацию пользователю, часто эффективнее получить ссылку в запросе, а строку сформировать уже при обходе выборки.
Рассмотрим пример на встроенном языке:
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
// Используем объектную модель метаданных для получения синонима
ИмяТипа = Выборка.ДокументСсылка.Метаданные().Синоним;
НужнаяСтрока = ИмяТипа + "_" + Выборка.Номер;
// ... дальнейшая логика
КонецЦикла;
Этот способ является наиболее производительным для базы данных, так как запрос остается максимально простым, а работа с метаданными в оперативной памяти сервера приложений происходит мгновенно.
В высоконагруженных системах, где требуется постоянная сортировка или поиск по текстовому представлению типа документа в больших реестрах, опытные разработчики применяют "хак": создают в документе (или в регистре сведений, если это реестр) дополнительный индексированный реквизит ИмяТипаСтрокой. При записи документа этот реквизит заполняется один раз, и в любых запросах вы просто обращаетесь к нему как к обычному текстовому полю. Это избавляет от любых проблем с ПРЕДСТАВЛЕНИЕ() или ВЫБОР КОГДА.
Подведем итог: прямого способа "одной кнопкой" превратить тип в строку внутри запроса 1С не существует из-за особенностей трансляции кода в SQL. Однако использование динамической сборки запроса или соединения с временной таблицей типов полностью решает эту задачу, сохраняя гибкость и чистоту вашего кода.