При разработке отчетов и обработок в типовых конфигурациях (например, «Управление производственным предприятием 1.3» или «Управление торговлей 10.3») часто возникает задача: взять определенный список позиций номенклатуры, определить для них значения конкретного дополнительного реквизита и затем найти в базе всю остальную номенклатуру, которая обладает точно такими же значениями этого свойства. Однако при попытке связать запросы через подзапрос в секции ГДЕ разработчики часто сталкиваются с тем, что запрос возвращает пустой результат или работает крайне медленно. Разберем типичные ошибки в таких конструкциях и рассмотрим правильные варианты реализации.
Рассмотрим исходный вариант запроса, вызвавший затруднения:
ВЫБРАТЬ
ЗначенияСвойствОбъектов.Значение КАК ЗначениеДопРеквизита
ПОМЕСТИТЬ СписокДопРеквизитов
ИЗ
РегистрСведений.ЗначенияСвойствОбъектов КАК ЗначенияСвойствОбъектов
ГДЕ
ЗначенияСвойствОбъектов.Объект В(&МассивНоменклатуры)
И ЗначенияСвойствОбъектов.Свойство = &Свойство
СГРУППИРОВАТЬ ПО
ЗначенияСвойствОбъектов.Значение
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ЗначенияСвойствОбъектов.Объект,
ЗначенияСвойствОбъектов.Значение
ИЗ
РегистрСведений.ЗначенияСвойствОбъектов КАК ЗначенияСвойствОбъектов
ГДЕ
ЗначенияСвойствОбъектов.Значение В
(ВЫБРАТЬ
СписокДопРеквизитов.ЗначениеДопРеквизита
ИЗ
СписокДопРеквизитов КАК СписокДопРеквизитов)
Проанализируем, почему этот код работает некорректно или не возвращает данные:
&МассивНоменклатуры передан пустой массив (количество элементов равно 0), условие В (&МассивНоменклатуры) в платформе гарантированно делает выборку пустой без генерации исключения. Кроме того, измерение Объект имеет составной тип. Если вместо ссылок на элементы справочника Номенклатура случайно переданы строки с кодами, артикулами или пустые ссылки другого типа, совпадений найдено не будет.Свойство = &Свойство). Ресурс Значение регистра сведений ЗначенияСвойствОбъектов имеет составной тип данных (числа, строки, булево, ссылки на справочник ЗначенияСвойствОбъектов). Если искать объекты только по полю Значение, запрос вернет ложные записи других объектов, у которых совпало значение совершенно другого свойства (например, логическая Истина, число 0 или одинаковая строка).В (ВЫБРАТЬ ...): использование подзапроса в блоке ГДЕ при работе с временными таблицами значительно уступает по производительности соединению таблиц и часто затрудняет оптимизатору СУБД построение эффективного плана выполнения.Оптимизируем логику: во втором запросе заменим условие В (ВЫБРАТЬ ...) на ВНУТРЕННЕЕ СОЕДИНЕНИЕ, добавим явный отбор по целевому свойству и проиндексируем поле связи во временной таблице. Также воспользуемся оператором ВЫРАЗИТЬ для однозначного приведения типа поля Объект.
ВЫБРАТЬ РАЗЛИЧНЫЕ
ЗначенияСвойствОбъектов.Значение КАК Значение
ПОМЕСТИТЬ ВТ_ЗначенияСвойств
ИЗ
РегистрСведений.ЗначенияСвойствОбъектов КАК ЗначенияСвойствОбъектов
ГДЕ
ЗначенияСвойствОбъектов.Объект В(&МассивНоменклатуры)
И ЗначенияСвойствОбъектов.Свойство = &Свойство
ИНДЕКСИРОВАТЬ ПО
Значение
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВЫРАЗИТЬ(ЗначенияСвойствОбъектов.Объект КАК Справочник.Номенклатура) КАК Номенклатура,
ЗначенияСвойствОбъектов.Значение КАК Значение
ИЗ
РегистрСведений.ЗначенияСвойствОбъектов КАК ЗначенияСвойствОбъектов
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ЗначенияСвойств КАК ВТ_ЗначенияСвойств
ПО ЗначенияСвойствОбъектов.Значение = ВТ_ЗначенияСвойств.Значение
ГДЕ
ЗначенияСвойствОбъектов.Свойство = &Свойство
И ЗначенияСвойствОбъектов.Объект ССЫЛКА Справочник.Номенклатура
Разберем ключевые моменты этой реализации:
ВЫБРАТЬ РАЗЛИЧНЫЕ исключает дублирование значений еще на этапе формирования временной таблицы.ИНДЕКСИРОВАТЬ ПО Значение существенно ускоряет операцию соединения в СУБД, особенно если выборка содержит большой объем данных.ЗначенияСвойствОбъектов.Свойство = &Свойство во втором запросе защищает от ложных срабатываний по одинаковым значениям других свойств.ССЫЛКА Справочник.Номенклатура и явное приведение типа через ВЫРАЗИТЬ(...) исключают попадание в выборку других сущностей (например, контрагентов, документов или складов), к которым также могут быть привязаны свойства.Если нет необходимости выполнять сложную постобработку данных или сохранять промежуточные результаты, можно обойтись без создания временных таблиц с помощью самосоединения (Self-Join) регистра сведений:
ВЫБРАТЬ РАЗЛИЧНЫЕ
ВЫРАЗИТЬ(ВсеОбъекты.Объект КАК Справочник.Номенклатура) КАК Номенклатура,
ВсеОбъекты.Значение КАК Значение
ИЗ
РегистрСведений.ЗначенияСвойствОбъектов КАК ИсходныеОбъекты
ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ЗначенияСвойствОбъектов КАК ВсеОбъекты
ПО ИсходныеОбъекты.Свойство = ВсеОбъекты.Свойство
И ИсходныеОбъекты.Значение = ВсеОбъекты.Значение
ГДЕ
ИсходныеОбъекты.Объект В(&МассивНоменклатуры)
И ИсходныеОбъекты.Свойство = &Свойство
И ВсеОбъекты.Объект ССЫЛКА Справочник.Номенклатура
Посмотрим на особенности этого подхода:
tempdb.Свойство, и по полю Значение, что гарантирует точное совпадение значений именно в рамках искомой характеристики.При работе с регистром ЗначенияСвойствОбъектов важно учитывать физическое устройство метаданных:
МассивНоменклатуры.Количество() > 0, и что параметр &Свойство является ссылкой на план видов характеристик СвойстваОбъектов.ВЫРАЗИТЬ(ЗначенияСвойствОбъектов.Значение КАК Строка(100)).И НЕ ВЫРАЗИТЬ(ВсеОбъекты.Объект КАК Справочник.Номенклатура).ПометкаУдаления.