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