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