При решении задач складского учета, ценообразования и маркетинга часто возникает необходимость найти элементы справочника, для которых еще не созданы соответствующие записи в регистре сведений (например, номенклатуру без цен, без штрихкодов или без сопутствующих товаров). Разберем, как правильно построить такой запрос, какие типичные ошибки допускают разработчики и в чем заключается ключевая разница между условиями в секциях ПО и ГДЕ.
Рассмотрим две самые распространенные ошибки, с которыми сталкиваются при попытке решить эту задачу:
Номенклатура, и справочник называется Номенклатура), обращение вида Номенклатура.Ссылка вызовет ошибку компиляции запроса «Неоднозначное поле». Всегда задавайте явные и уникальные псевдонимы для каждой таблицы в секции ИЗ (например, СпрНоменклатура).ПО Регистр.Номенклатура ЕСТЬ NULL является принципиальной ошибкой в логике работы реляционных соединений. Такое условие проверяет исходные данные в присоединяемой таблице, а не факт отсутствия соответствия.Разберем стандартный и наиболее понятный алгоритм решения. Для получения элементов, которых нет во второй таблице, основной (левой) таблицей должен выступать Справочник.Номенклатура, а присоединяемой (правой) — РегистрСведений. Сопоставление выполняется по равенству ссылок, а отсечение существующих записей — в секции ГДЕ.
Посмотрим на пример правильного текста запроса:
ВЫБРАТЬ
СпрНоменклатура.Ссылка КАК Номенклатура
ИЗ
Справочник.Номенклатура КАК СпрНоменклатура
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.СопутствующиеТовары КАК СопутствующиеТовары
ПО СпрНоменклатура.Ссылка = СопутствующиеТовары.Номенклатура
ГДЕ
СопутствующиеТовары.Номенклатура ЕСТЬ NULL
И НЕ СпрНоменклатура.ЭтоГруппа
И НЕ СпрНоменклатура.ПометкаУдаления
Проанализируем, как работает этот запрос:
ЛЕВОЕ СОЕДИНЕНИЕ гарантирует, что в выборку попадут абсолютно все элементы справочника.СпрНоменклатура.Ссылка = СопутствующиеТовары.Номенклатура СУБД сопоставляет строки. Если запись в регистре найдена, поля регистра заполняются фактическими данными. Если записи нет, во все поля регистра для данной строки подставляется специальное значение NULL.СопутствующиеТовары.Номенклатура ЕСТЬ NULL срабатывает уже после формирования соединенных строк и оставляет только те позиции, для которых соответствие в регистре не было найдено.Выясним причину, почему нельзя писать условие проверки на NULL внутри соединения ПО:
ПО СопутствующиеТовары.Номенклатура ЕСТЬ NULL, СУБД будет искать в регистре физически пустые записи. Однако при левом соединении строки левой таблицы (справочника) всё равно останутся в выборке, даже если условие не выполнилось. В итоге результат будет содержать всю номенклатуру, а отсечения существующих позиций не произойдет.ЕСТЬ NULL отсекает строки, для которых нашлись реальные данные в регистре.Другой способ получить тот же результат — использование условия вложенного подзапроса с оператором НЕ В. Рассмотрим этот пример:
ВЫБРАТЬ
СпрНоменклатура.Ссылка КАК Номенклатура
ИЗ
Справочник.Номенклатура КАК СпрНоменклатура
ГДЕ
СпрНоменклатура.Ссылка НЕ В
(ВЫБРАТЬ РАЗЛИЧНЫЕ
СопутствующиеТовары.Номенклатура
ИЗ
РегистрСведений.СопутствующиеТовары КАК СопутствующиеТовары)
И НЕ СпрНоменклатура.ЭтоГруппа
И НЕ СпрНоменклатура.ПометкаУдаления
Такой вариант читается декларативно проще, однако при очень больших объемах регистра может уступать по производительности левому соединению, если СУБД построит неоптимальный план выполнения.
Разберем практические нюансы, которые необходимо учитывать при написании запросов к справочникам и регистрам в реальных базах данных:
Номенклатура почти всегда является иерархическим. Если не добавить условие НЕ СпрНоменклатура.ЭтоГруппа, в результирующий список попадут папки (группы), так как в регистре они обычно не фигурируют.НЕ СпрНоменклатура.ПометкаУдаления.Номенклатура является первым измерением непериодического регистра, СУБД использует кластерный индекс. Если же поле является вторым измерением или реквизитом, необходимо убедиться, что для него включено свойство «Ведущее» или «Индексировать», иначе запрос вызовет полное сканирование таблицы регистра (Table Scan).ЦеныНоменклатуры), прямое обращение к физической таблице проверит наличие записей за всю историю. Если необходимо узнать, нет ли актуальной записи на определенную дату, соединяться следует не с физической таблицей регистра, а с виртуальной таблицей СрезПоследних.МенеджерВременныхТаблиц, проиндексировать ее по полю номенклатуры и только затем выполнять ЛЕВОЕ СОЕДИНЕНИЕ.УстановитьПривилегированныйРежим(Истина).