Как в запросе 1С отобрать записи с заполненным полем составного типа «Любой документ»?

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

При работе с регистрами сведений или табличными частями в 1С часто встречается ситуация, когда измерение или реквизит имеет составной тип данных, например, ДокументСсылка (так называемый «Любой документ»). Особенность таких полей заключается в том, что понятие «пустое значение» для них неоднозначно. В этой статье мы подробно разберем, как правильно составить запрос для отбора записей, в которых такое поле заполнено, и проанализируем нюансы платформенного хранения данных.

Понятие «пустоты» в составных типах данных

Прежде чем приступать к написанию запроса, нам необходимо разобраться, что именно мы считаем «незаполненным» значением. В случае с одиночным типом (например, только ДокументСсылка.РеализацияТоваровУслуг) все просто — значение либо есть, либо это ПустаяСсылка. Однако в составном типе (Любой документ) вариантов отсутствия данных может быть несколько:

  1. Неопределено (Undefined): Это базовое состояние поля, когда в него вообще не был записан объект ни одного из типов. Такое значение часто встречается в новых записях или если тип значения еще не был выбран пользователем.
  2. Пустая ссылка конкретного типа: Если в поле был выбран конкретный вид документа (например, «Поступление товаров»), но сам объект не выбран, в базе сохраняется пустая ссылка именно этого типа.
  3. NULL: Появляется в результатах запроса при использовании ЛЕВОЕ СОЕДИНЕНИЕ, если во второй таблице нет соответствующей записи.

Проанализируем ситуацию: чтобы получить только «заполненные» записи, нам нужно исключить все три вышеперечисленных варианта.

Способ 1. Использование сравнения с Неопределено и массивом пустых ссылок

Это классический и наиболее надежный способ, который подходит для большинства задач. Суть заключается в том, что мы в параметрах запроса передаем массив, содержащий пустые ссылки всех возможных типов документов, входящих в составное поле.

Рассмотрим по шагам, как это реализовать. Сначала подготовим массив пустых ссылок на стороне встроенного языка:


МассивПустых = Новый Массив;
// Перебираем метаданные всех документов в конфигурации
Для Каждого МетаДанныеДокумента Из Метаданные.Документы Цикл
    // Получаем пустую ссылку для каждого вида документа и добавляем в массив
    МассивПустых.Добавить(Документы[МетаДанныеДокумента.Имя].ПустаяСсылка());
КонецЦикла;

Теперь составим текст запроса, в котором отсечем и Неопределено, и значения из нашего массива:


ВЫБРАТЬ
    Регистр.Период,
    Регистр.ОбъектДокумент
ИЗ
    РегистрСведений.ИмяРегистра КАК Регистр
ГДЕ
    Регистр.ОбъектДокумент <> НЕОПРЕДЕЛЕНО
    И НЕ Регистр.ОбъектДокумент В (&МассивПустых)

В данном примере условие <> НЕОПРЕДЕЛЕНО отсекает записи без установленного типа, а условие НЕ В (&МассивПустых) убирает пустые ссылки всех типов документов. Обратите внимание, что в языке запросов 1С ключевое слово НЕОПРЕДЕЛЕНО доступно напрямую, и его не обязательно передавать как параметр.

Способ 2. Применение функции ТИПЗНАЧЕНИЯ

Разберем альтернативный подход. Иногда нам нужно не просто исключить пустые значения, а убедиться, что тип значения в поле соответствует определенным критериям. В языке запросов есть функция ТИПЗНАЧЕНИЯ, которая возвращает тип данных текущей записи.

Если мы хотим убедиться, что поле заполнено хоть каким-то документом, мы можем проверить, что тип этого поля не равен типу Неопределено и не равен NULL (если есть соединения). Однако этот способ сам по себе не исключает пустые ссылки конкретных типов. Поэтому его лучше использовать как вспомогательный инструмент для фильтрации типов.

Нюансы хранения данных на уровне СУБД (SQL)

Выясним причину, почему составные типы работают медленнее обычных. На уровне SQL Server или PostgreSQL поле составного типа хранится не в одной колонке, а в нескольких:

Когда мы пишем условие ГДЕ Поле <> НЕОПРЕДЕЛЕНО, СУБД проверяет колонку с типом. Если там стоит значение, соответствующее Неопределено (в SQL это часто представлено специфическим набором полей), запись отсекается. Использование ЕСТЬ NULL в запросе проверяет физическое отсутствие записи в таблице (результат соединения), что является самым быстрым способом проверки на уровне базы данных.

Проблема «битых» ссылок

Рассмотрим подробнее ситуацию, которую часто упускают из виду. Существует еще один вид «пустоты» — Объект не найден. Это происходит, когда ссылка в регистре есть, но сам документ был физически удален из базы без контроля ссылочной целостности.

С точки зрения запроса:

Следовательно, стандартный фильтр пропустит такие «битые» ссылки как заполненные. Если ваша задача требует исключить и их, то единственным верным (но ресурсоемким) способом будет ЛЕВОЕ СОЕДИНЕНИЕ со всеми таблицами документов, входящими в состав, и проверка на ЕСТЬ NULL по реквизиту Ссылка из этих таблиц.

Рекомендации по производительности

Проанализируем ситуацию с большими объемами данных. Если ваш регистр содержит миллионы записей, отбор по условию НЕ В (&МассивПустых) может привести к существенным задержкам, так как индексы по составным полям при использовании отрицания (НЕ) работают неэффективно.

Для оптимизации мы рекомендуем следующее:

  1. Дополнительный флаг: Добавьте в регистр измерение или реквизит Заполнено (тип Булево). Устанавливайте его в значение Истина в событии ПередЗаписью, если документ выбран и ссылка не пустая. Запрос по булеву полю будет выполняться мгновенно.
  2. Разделение типов: Если это возможно по архитектуре, старайтесь избегать типа «Любой документ», заменяя его на несколько полей с конкретными типами или используя механизм характеристик (ПВХ).
  3. Ограничение списка документов: Если вы заранее знаете, что в данном поле могут быть ссылки только на 5-10 видов документов (а не на все 500, существующие в системе), наполняйте массив пустых ссылок только этими типами. Это сократит количество параметров в SQL-запросе.

Итоговый пример кода для использования в обработке

Сведем все знания в единый пример кода, который можно использовать как готовый шаблон:


Процедура ВыполнитьОтборЗаполненных(Элемент)
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   Т.Период КАК Период,
        |   Т.ДокументОснование КАК Документ
        |ИЗ
        |   РегистрСведений.ИсторияСтатусов КАК Т
        |ГДЕ
        |   Т.ДокументОснование <> НЕОПРЕДЕЛЕНО
        |   И НЕ Т.ДокументОснование В (&МассивПустых)";
    
    // Формируем список пустых ссылок
    МассивПустых = Новый Массив;
    Для Каждого МетаДок Из Метаданные.Документы Цикл
        МассивПустых.Добавить(Документы[МетаДок.Имя].ПустаяСсылка());
    КонецЦикла;
    
    Запрос.УстановитьПараметр("МассивПустых", МассивПустых);
    
    Результат = Запрос.Выполнить();
    // Дальнейшая обработка результата...
    
КонецПроцедуры

Таким образом, мы рассмотрели все основные способы работы с составными типами в запросах. Помните, что правильная обработка Неопределено и пустых ссылок — залог корректности ваших отчетов и алгоритмов в 1С.

← На главную