Почему не работает условие фильтрации по "ПометкеУдаления" во вложенном запросе 1С?

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

При работе со сложными запросами в 1С, особенно включающими вложенные запросы и оператор НЕ В, мы часто сталкиваемся с ситуациями, когда условия фильтрации срабатывают не так, как ожидалось. Одна из частых причин — неправильное указание пути к реквизитам объекта, особенно если мы работаем с данными табличных частей документов. В этой статье мы подробно разберем типичную проблему, когда условие НЕ Объект.ПометкаУдаления во вложенном запросе не дает нужного результата, и выясним, как ее правильно решить.

Анализируем исходную проблему: Некорректное условие во вложенном запросе

Рассмотрим исходный запрос, в котором возникла проблема. Мы хотим выбрать проведенные и непроведенные заказы покупателей за определенный период, которые не связаны с определенными документами (Маршрутными листами или Листами склада). Вот фрагмент запроса, который вызывал вопросы:


ВЫБРАТЬ
    ЗаказПокупателя.Ссылка
ИЗ
    Документ.ЗаказПокупателя КАК ЗаказПокупателя
ГДЕ
    ЗаказПокупателя.Проведен
    И НЕ ЗаказПокупателя.ПометкаУдаления
    И ЗаказПокупателя.Дата МЕЖДУ &ДатаС И &ДатаПо
    И (НЕ ЗаказПокупателя.Ссылка В
        (ВЫБРАТЬ
            МаршрутныйЛист.ДокументОснование
        ИЗ
            Документ.МаршрутныйЛист КАК МаршрутныйЛист
        ГДЕ
            НЕ МаршрутныйЛист.ПометкаУдаления)
        ИЛИ НЕ ЗаказПокупателя.Ссылка В
        (ВЫБРАТЬ
            ЛистСклада.ЗаказПокупателя
        ИЗ
            Документ.ЛистСклада.Данные КАК ЛистСклада
        ГДЕ
            НЕ ЗаказПокупателя.ПометкаУдаления) // <-- Здесь была ошибка!
    )

Автор запроса столкнулся с тем, что часть условия, касающаяся ЛистСклада, не работала, а именно фильтрация по ПометкаУдаления внутри этого вложенного запроса. Мы можем видеть, что в условии ГДЕ вложенного запроса используется НЕ ЗаказПокупателя.ПометкаУдаления. В данном контексте ЗаказПокупателя ссылается на объект из основного запроса, а не на какой-либо объект, связанный с вложенным запросом о ЛистСклада. Это ключевая ошибка, так как система запросов не может определить, к какому именно ЗаказПокупателя относится это условие внутри вложенного запроса, который оперирует с ЛистСклада.Данные.

Разбираемся с объектными ссылками во вложенных запросах

Чтобы понять, как исправить ошибку, давайте глубже разберем, как мы должны обращаться к реквизитам различных объектов во вложенных запросах, особенно когда работаем с табличными частями:

  1. Ссылка на табличную часть: Когда мы пишем ИЗ Документ.ЛистСклада.Данные КАК ЛистСклада, псевдоним ЛистСклада в этом контексте относится к строке табличной части документа ЛистСклада, а не к самому документу-владельцу. Строка табличной части не имеет напрямую реквизита ПометкаУдаления.

  2. Ссылка на документ-владелец табличной части: Если мы хотим проверить реквизит самого документа ЛистСклада (который является владельцем табличной части Данные), то нам нужно обратиться к нему через поле Ссылка табличной части. То есть, путь будет выглядеть как ЛистСклада.Ссылка.ПометкаУдаления. Это позволит нам получить ссылку на документ ЛистСклада и проверить его пометку удаления.

  3. Ссылка на связанный документ: В табличной части Документ.ЛистСклада.Данные часто присутствует реквизит, который ссылается на другой документ, например, ЗаказПокупателя (как в нашем случае ЛистСклада.ЗаказПокупателя). Если мы хотим проверить пометку удаления именно этого связанного ЗаказПокупателя, то путь будет ЛистСклада.ЗаказПокупателя.ПометкаУдаления.

В нашем случае, исходная запись НЕ ЗаказПокупателя.ПометкаУдаления внутри вложенного запроса была неверна, потому что ЗаказПокупателя в этом контексте относился к внешнему запросу, а не к текущей обрабатываемой строке ЛистСклада.Данные.

Исправляем проблему: Корректное условие фильтрации

Основная проблема заключалась в неправильной идентификации объекта, для которого проверялась пометка удаления. Автор запроса, после анализа, пришел к выводу, что нужно проверять пометку удаления самого документа "Лист склада", а не некоррелированного "Заказа покупателя". Соответственно, корректное условие во вложенном запросе должно выглядеть так:


// ...
ИЛИ НЕ ЗаказПокупателя.Ссылка В
(ВЫБРАТЬ
    ЛистСклада.ЗаказПокупателя
ИЗ
    Документ.ЛистСклада.Данные КАК ЛистСклада
ГДЕ
    НЕ ЛистСклада.Ссылка.ПометкаУдаления) // <-- Исправленное условие

Здесь ЛистСклада.Ссылка дает нам ссылку на документ-владелец табличной части Данные, и уже от этой ссылки мы можем проверить реквизит ПометкаУдаления. Это позволяет вложенному запросу отбирать только те ЗаказПокупателя, которые находятся в непроведенных или непомеченных на удаление ЛистахСклада.

Таким образом, полный исправленный запрос будет выглядеть следующим образом:


ВЫБРАТЬ
    ЗаказПокупателя.Ссылка
ИЗ
    Документ.ЗаказПокупателя КАК ЗаказПокупателя
ГДЕ
    ЗаказПокупателя.Проведен
    И НЕ ЗаказПокупателя.ПометкаУдаления
    И ЗаказПокупателя.Дата МЕЖДУ &ДатаС И &ДатаПо
    И (НЕ ЗаказПокупателя.Ссылка В
        (ВЫБРАТЬ
            МаршрутныйЛист.ДокументОснование
        ИЗ
            Документ.МаршрутныйЛист КАК МаршрутныйЛист
        ГДЕ
            НЕ МаршрутныйЛист.ПометкаУдаления)
        ИЛИ НЕ ЗаказПокупателя.Ссылка В
        (ВЫБРАТЬ
            ЛистСклада.ЗаказПокупателя
        ИЗ
            Документ.ЛистСклада.Данные КАК ЛистСклада
        ГДЕ
            НЕ ЛистСклада.Ссылка.ПометкаУдаления) // <-- Исправленное условие
    )

Важно понимать, что если бы наша бизнес-логика требовала фильтровать заказы по пометке удаления самого заказа покупателя, который указан в строке листа склада, то тогда корректным условием было бы НЕ ЛистСклада.ЗаказПокупателя.ПометкаУдаления. Выбор правильного пути зависит от того, какой именно объект мы хотим проверять на пометку удаления: документ-владелец табличной части, или документ, на который ссылается реквизит табличной части.

Рекомендации по логике условий: И против ИЛИ

Также важно обратить внимание на логику использования операторов И и ИЛИ. В оригинальном запросе используется И (НЕ ЗаказПокупателя.Ссылка В (...) ИЛИ НЕ ЗаказПокупателя.Ссылка В (...)).


ВЫБРАТЬ
    ЗаказПокупателя.Ссылка
ИЗ
    Документ.ЗаказПокупателя КАК ЗаказПокупателя
ГДЕ
    ЗаказПокупателя.Проведен
    И НЕ ЗаказПокупателя.ПометкаУдаления
    И ЗаказПокупателя.Дата МЕЖДУ &ДатаС И &ДатаПо
    И (НЕ ЗаказПокупателя.Ссылка В
        (ВЫБРАТЬ
            МаршрутныйЛист.ДокументОснование
        ИЗ
            Документ.МаршрутныйЛист КАК МаршрутныйЛист
        ГДЕ
            НЕ МаршрутныйЛист.ПометкаУдаления)
    И  НЕ ЗаказПокупателя.Ссылка В // <-- Изменено на И
        (ВЫБРАТЬ
            ЛистСклада.ЗаказПокупателя
        ИЗ
            Документ.ЛистСклада.Данные КАК ЛистСклада
        ГДЕ
            НЕ ЛистСклада.Ссылка.ПометкаУдаления)
    )

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

Повышение производительности и читаемости сложных запросов

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

  1. Использование временных таблиц: Создание временных таблиц из результатов вложенных запросов и последующее их соединение с основной таблицей часто делает запрос более понятным и оптимизированным. Это позволяет системе управления базами данных (СУБД) эффективнее обрабатывать данные.

    Разберем по шагам, как это можно сделать:

    1. Сначала мы формируем временную таблицу для "Маршрутных листов", собирая ссылки на основания документов:

      
      ВЫБРАТЬ
          МаршрутныйЛист.ДокументОснование КАК ЗаказПокупателя
      ПОМЕСТИТЬ ВТ_ЗаказыИзМаршрутныхЛистов
      ИЗ
          Документ.МаршрутныйЛист КАК МаршрутныйЛист
      ГДЕ
          НЕ МаршрутныйЛист.ПометкаУдаления
      ;
      
    2. Затем мы формируем временную таблицу для "Листов склада", собирая ссылки на заказы покупателей из непроведенных или непомеченных на удаление листов склада:

      
      ВЫБРАТЬ
          ЛистСклада.ЗаказПокупателя КАК ЗаказПокупателя
      ПОМЕСТИТЬ ВТ_ЗаказыИзЛистовСклада
      ИЗ
          Документ.ЛистСклада.Данные КАК ЛистСклада
      ГДЕ
          НЕ ЛистСклада.Ссылка.ПометкаУдаления
      ;
      
    3. Теперь мы можем объединить эти временные таблицы в основной запрос, используя ЛЕВОЕ СОЕДИНЕНИЕ и условие ЕСТЬ NULL для исключения записей:

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

      Обратите внимание, что здесь условие ИЛИ между двумя ЕСТЬ NULL соответствует логике НЕ A ИЛИ НЕ B. Если бы мы хотели НЕ A И НЕ B (то есть, чтобы заказ не был ни в одном из списков), мы бы использовали И между условиями ЕСТЬ NULL.

  2. Использование ЛЕВОЕ СОЕДИНЕНИЕ с ЕСТЬ NULL: Этот метод является более предпочтительным, чем НЕ В для больших объемов данных, так как СУБД может оптимизировать соединения более эффективно. Принцип заключается в том, что мы пытаемся соединить основную таблицу с таблицами, из которых хотим исключить записи. Если соединение не находит соответствия (то есть, объект отсутствует в исключаемом списке), то поля из присоединенной таблицы будут NULL, и мы можем использовать это в условии ГДЕ.

Методы отладки сложных запросов в 1С

Для эффективной отладки запросов в 1С, особенно когда возникают подобные проблемы, мы рекомендуем использовать следующий подход:

  1. Консоль запросов: Это ваш основной инструмент. Позволяет выполнить запрос по частям, увидеть структуру и данные, возвращаемые каждым вложенным запросом или временной таблицей. Вы можете пошагово проверять, что каждый фрагмент запроса работает так, как ожидается, и какие именно данные он возвращает.

  2. Выделение и выполнение частей запроса: Если у вас есть сложный запрос с несколькими вложенными блоками ВЫБРАТЬ, попробуйте выполнить каждый из них по отдельности в консоли. Это поможет убедиться, что каждый вложенный запрос возвращает именно те данные, которые вы ожидаете для дальнейшей фильтрации или соединения.

  3. Проверка условий ГДЕ: В случае, когда условие НЕ ... ПометкаУдаления не работает, выполните соответствующий вложенный запрос без этого условия, чтобы увидеть все данные. Затем добавьте условие ГДЕ и проверьте, как оно влияет на выборку. Это поможет изолировать проблему и точно определить, какая часть условия работает не так.

Всегда будьте внимательны к путям к реквизитам и контексту объектов в каждом блоке запроса. Понимание структуры данных и использование эффективных методов отладки помогут вам быстро находить и исправлять подобные ошибки.

← На главную