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