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

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

При разработке отчетов и обработок в среде «1С:Предприятие 8» программисты часто сталкиваются с задачей: получить список документов за определенный период и одновременно вывести содержимое их табличной части. При этом критически важно вывести даже те документы, у которых эта табличная часть совершенно пуста.

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

Почему стандартный запрос не выбирает документы с пустой табличной частью?

Проанализируем типичную ситуацию. Нам нужно выбрать документы РеализацияТоваровУслуг за определенный период и получить данные их табличной части НомераИзделий. Если мы попытаемся связать их напрямую, используя левое или полное соединение, но при этом установим условия фильтрации в секции ГДЕ на поля табличной части, то документы с пустыми строками исчезнут.

Рассмотрим, почему это происходит. Когда мы выполняем левое соединение (ЛЕВОЕ СОЕДИНЕНИЕ) таблицы документов (слева) с таблицей их табличной части (справа), для документов, у которых табличная часть пуста, СУБД возвращает значения NULL во всех полях правой таблицы.

Если в секции ГДЕ мы пишем условие фильтрации по дате или пометке удаления, обращаясь к правой таблице:


ГДЕ
    РеализацияТоваровУслугНомераИзделий.Ссылка.Дата МЕЖДУ &ДатаНач И &ДатаКон
    И РеализацияТоваровУслугНомераИзделий.Ссылка.ПометкаУдаления = ЛОЖЬ

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

Способ 1. Классическое левое соединение с правильной фильтрацией

Самое простое и быстрое решение — перенести условия фильтрации на левую (основную) таблицу документов и правильно составить связи. Выясним, как это реализовать на практике.

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


ВЫБРАТЬ
    Реализация.Ссылка КАК ДокРеализации,
    ЕСТЬNULL(НомераИзделий.Номенклатура, ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)) КАК Номенклатура,
    ЕСТЬNULL(НомераИзделий.НомерИзделия, "") КАК НомерИзделия
ИЗ
    Документ.РеализацияТоваровУслуг КАК Реализация
    ЛЕВОЕ СОЕДИНЕНИЕ Документ.ReplicaТоваровУслуг.НомераИзделий КАК НомераИзделий
        ПО Реализация.Ссылка = НомераИзделий.Ссылка
ГДЕ
    Реализация.Дата МЕЖДУ &ДатаНач И &ДатаКон
    И НЕ Реализация.ПометкаУдаления

Проанализируем ключевые моменты этого запроса:

  1. Главная таблица слева. В качестве ведущей таблицы мы выбрали Документ.РеализацияТоваровУслуг. СУБД гарантированно вернет все документы, подходящие под условия секции ГДЕ.
  2. Правильные условия в ГДЕ. Все условия по дате периода и пометке удаления наложены исключительно на поля левой таблицы шапки (Реализация.Дата и Реализация.ПометкаУдаления). Так как в этих полях гарантированно нет значений NULL, фильтрация отработает корректно.
  3. Обработка NULL-значений. Мы применили функцию ЕСТЬNULL. Если табличная часть пуста, вместо системного значения NULL в результат запроса вернется пустая ссылка справочника номенклатуры или пустая строка для номера изделия. Это убережет ваш дальнейший код от ошибок типов данных.

Способ 2. Профессиональная оптимизация через временные таблицы

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

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

Рассмотрим этот эффективный алгоритм:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |    Реализация.Ссылка КАК Ссылка
    |ПОМЕСТИТЬ ВТ_Документы
    |ИЗ
    |    Документ.РеализацияТоваровУслуг КАК Реализация
    |ГДЕ
    |    Реализация.Дата МЕЖДУ &ДатаНач И &ДатаКон
    |    И НЕ Реализация.ПометкаУдаления
    |
    |ИНДЕКСИРОВАТЬ ПО
    |    Ссылка
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |    ВТ_Документы.Ссылка КАК ДокРеализации,
    |    ЕСТЬNULL(НомераИзделий.Номенклатура, ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)) КАК Номенклатура,
    |    ЕСТЬNULL(НомераИзделий.НомерИзделия, """") КАК НомерИзделия
    |ИЗ
    |    ВТ_Документы КАК ВТ_Документы
    |        ЛЕВОЕ СОЕДИНЕНИЕ Документ.РеализацияТоваровУслуг.НомераИзделий КАК НомераИзделий
    |        ПО ВТ_Документы.Ссылка = НомераИзделий.Ссылка";

Запрос.УстановитьПараметр("ДатаНач", НачалоДня(ДатаНачала));
Запрос.УстановитьПараметр("ДатаКон", КонецДня(ДатаОкончания));
РезультатЗапроса = Запрос.Выполнить();

Выясним преимущества такого подхода:

Способ 3. Выборка вложенной таблицы (Синтаксический сахар 1С)

Существует еще один изящный способ, который позволяет полностью отказаться от явного прописывания левых соединений в тексте запроса. Платформа «1С:Предприятие 8» умеет самостоятельно выбирать табличные части документов в виде вложенных таблиц.

Посмотрим на пример реализации такого запроса:


ВЫБРАТЬ
    Реализация.Ссылка КАК ДокРеализации,
    Реализация.НомераИзделий.(
        Номенклатура КАК Номенклатура,
        НомерИзделия КАК НомерИзделия
    ) КАК ТЧ_НомераИзделий
ИЗ
    Документ.РеализацияТоваровУслуг КАК Реализация
ГДЕ
    Реализация.Date МЕЖДУ &ДатаНач И &ДатаКон
    И НЕ Реализация.ПометкаУдаления

Проанализируем особенности работы этого механизма:

Важная архитектурная ошибка: Обращение через точку

Разбирая исходный некорректный запрос автора, нельзя обойти стороной еще одну серьезную ошибку оптимизации. В тексте присутствовало обращение: РеализацияТоваровУслугНомераИзделий.Ссылка.Дата.

Помните: обращение «через точку» от полей табличной части к реквизитам шапки документа (ТабличнаяЧасть.Ссылка.ИмяРеквизита) — это грубое нарушение стандартов разработки 1С. На уровне СУБД такое обращение заставляет платформу создавать дополнительное неявное соединение таблиц SQL. На больших объемах данных это гарантированно приводит к резкому падению производительности и лишней нагрузке на сервер.

Всегда обращайтесь к реквизитам шапки непосредственно через основную таблицу документа (например, Реализация.Дата), как это показано в правильных примерах выше.

Резюме

Мы рассмотрели, как правильно выбирать документы вместе с их табличными частями, избегая досадной потери данных при пустых таблицах. Для простых задач используйте классическое левое соединение с фильтрацией строго по левой таблице (Способ 1). В высоконагруженных системах и при работе с большими периодами времени всегда отдавайте предпочтение временным таблицам с индексацией (Способ 2). А если вам необходимо получить иерархическую структуру данных без дублирования шапки документа — смело используйте вложенные таблицы выборки (Способ 3).

← На главную