Как в запросе 1С выбрать записи, для которых отсутствуют данные в подчиненной таблице?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В практике разработчика 1С часто возникает задача: найти объекты в основной таблице (например, в справочнике Контрагенты), для которых не заведено ни одной записи в подчиненной таблице (например, в справочнике ИнформационныеБазы или в определенном регистре сведений). На первый взгляд задача кажется простой, но именно на таких «очевидных» вещах часто случается логический ступор. Давайте подробно разберем, как правильно составить такой запрос, какие существуют нюансы и как оптимизировать производительность при больших объемах данных.

Метод 1: Использование ЛЕВОГО СОЕДИНЕНИЯ и проверки на NULL

Это самый распространенный и эффективный способ решения задачи. Разберем логику: мы берем всех контрагентов и пытаемся «приклеить» к ним записи из подчиненного справочника. Если у контрагента есть подчиненные элементы, поля из правой таблицы заполнятся данными. Если подчиненных элементов нет, поля правой таблицы примут значение NULL. Нам остается лишь отфильтровать результат, оставив только те строки, где в правой части стоит «пустота».

Рассмотрим пошагово составление такого запроса:

  1. Выбираем основную таблицу (владельца) — Справочник.Контрагенты.
  2. Применяем ЛЕВОЕ СОЕДИНЕНИЕ к подчиненной таблице — Справочник.ИнформационныеБазы.
  3. Указываем условие связи: Контрагенты.Ссылка = ИнформационныеБазы.Владелец.
  4. Добавляем условие ГДЕ, в котором проверяем ссылку из подчиненной таблицы на ЕСТЬ NULL.

Проанализируем пример кода для нашей задачи:


ВЫБРАТЬ
    Контрагенты.Ссылка КАК Ссылка,
    Контрагенты.Наименование КАК Наименование
ИЗ
    Справочник.Контрагенты КАК Контрагенты
        ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ИнформационныеБазы КАК ИнформационныеБазы
        ПО Контрагенты.Ссылка = ИнформационныеБазы.Владелец
ГДЕ
    ИнформационныеБазы.Ссылка ЕСТЬ NULL

Важный момент: Проверять на NULL нужно именно поле Ссылка (или любое другое поле, которое гарантированно заполнено в подчиненной таблице). Если вы проверите какой-то реквизит, который сам по себе может быть не заполнен, вы получите неверный результат.

Метод 2: Использование конструкции НЕ В (подзапрос)

Если логика с соединениями кажется вам менее наглядной, можно воспользоваться конструкцией НЕ В. С точки зрения «человеческого» языка это звучит так: «Выбери всех контрагентов, чья ссылка не встречается в списке владельцев справочника информационных баз».

Давайте посмотрим на пример реализации:


ВЫБРАТЬ
    Контрагенты.Ссылка
ИЗ
    Справочник.Контрагенты КАК Контрагенты
ГДЕ
    Контрагенты.Ссылка НЕ В
        (ВЫБРАТЬ
            ИнформационныеБазы.Владелец
        ИЗ
            Справочник.ИнформационныеБазы КАК ИнформационныеБазы)

Хотя этот вариант читается легче, следует учитывать вопросы производительности. В СУБД MS SQL Server оптимизаторы обычно хорошо справляются с обоими вариантами, но на старых версиях платформы или специфических СУБД ЛЕВОЕ СОЕДИНЕНИЕ часто отрабатывает быстрее, чем NOT IN, особенно если в подчиненной таблице миллионы записей.

Различие между NULL и Пустой ссылкой

Начинающие разработчики часто путают эти два понятия, что приводит к пустым результатам запроса. Выясним причину этой разницы:

При использовании ЛЕВОГО СОЕДИНЕНИЯ, если связь не сработала, система возвращает именно NULL. Сравнение ИнформационныеБазы.Ссылка = ЗНАЧЕНИЕ(Справочник.ИнформационныеБазы.ПустаяСсылка) в блоке ГДЕ не поможет найти контрагентов без баз, так как NULL не равен пустой ссылке.

Учет пометок на удаление

Проанализируем ситуацию, когда у контрагента есть информационные базы, но все они помечены на удаление. Если бизнес-логика требует считать такого контрагента «пустым», нам нужно изменить условие соединения.

Внимание: Условие на отсутствие пометки удаления нужно ставить именно в блоке ПО (условие связи), а не в блоке ГДЕ. Рассмотрим почему:


ВЫБРАТЬ
    Контрагенты.Ссылка
ИЗ
    Справочник.Контрагенты КАК Контрагенты
        ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ИнформационныеБазы КАК ИнформационныеБазы
        ПО Контрагенты.Ссылка = ИнформационныеБазы.Владелец
            И ИнформационныеБазы.ПометкаУдаления = ЛОЖЬ
ГДЕ
    ИнформационныеБазы.Ссылка ЕСТЬ NULL

Если мы перенесем ИнформационныеБазы.ПометкаУдаления = ЛОЖЬ в ГДЕ, то запрос сначала соединит все записи, а потом отфильтрует те, где есть пометка. В итоге мы потеряем те строки, где ИнформационныеБазы.Ссылка ЕСТЬ NULL, так как NULL не может быть равен ЛОЖЬ. Указывая условие в ПО, мы заставляем систему соединять контрагента только с «живыми» базами. Если «живых» нет — получим NULL, что нам и нужно.

Оптимизация для больших справочников

Если в вашей базе данных сотни тысяч контрагентов и столько же записей в подчиненном справочнике, прямое соединение может нагрузить сервер. В профессиональной разработке 1С рекомендуется использовать временные таблицы. Разберем этот подход по шагам:

  1. Сначала выберем все уникальные ссылки владельцев из подчиненного справочника во временную таблицу ВТ_Владельцы.
  2. Обязательно проиндексируем это поле во временной таблице.
  3. Соединим основную таблицу Контрагенты с ВТ_Владельцы левым соединением.

Пример оптимизированного запроса:


ВЫБРАТЬ РАЗЛИЧНЫЕ
    ИнформационныеБазы.Владелец КАК Владелец
ПОМЕСТИТЬ ВТ_Владельцы
ИЗ
    Справочник.ИнформационныеБазы КАК ИнформационныеБазы
ИНДЕКСИРОВАТЬ ПО
    Владелец
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Контрагенты.Ссылка
ИЗ
    Справочник.Контрагенты КАК Контрагенты
        ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Владельцы КАК ВТ_Владельцы
        ПО Контрагенты.Ссылка = ВТ_Владельцы.Владелец
ГДЕ
    ВТ_Владельцы.Владелец ЕСТЬ NULL

Такой подход значительно ускоряет работу, так как СУБД будет сравнивать данные с предварительно подготовленным и сжатым списком, а не сканировать всю таблицу подчиненного справочника целиком при каждом сопоставлении.

Подводя итог, можно сказать, что решение проблемы «тупняка» при составлении подобных запросов заключается в четком понимании работы ЛЕВОГО СОЕДИНЕНИЯ. Помните о NULL, правильно работайте с условиями пометки удаления и не забывайте про временные таблицы в высоконагруженных системах.

← На главную