Как отобрать документы, если в их табличной части есть одна операция, но нет другой?

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

Приветствуем вас! Сегодня мы рассмотрим распространенную задачу в мире 1С: как правильно составить запрос, чтобы отобрать документы, в табличной части которых одновременно присутствует определенная операция (например, "Фасовка"), но при этом полностью отсутствует другая операция (например, "Отправка"). Эта задача часто вызывает затруднения, поскольку прямое использование условий в секции ГДЕ может привести к неожиданным результатам. Давайте разберем эту проблему и предложим несколько эффективных решений.

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


ВЫБРАТЬ
    ВыполнениеРаботОперации.Ссылка КАК Ссылка,
    ВыполнениеРаботОперации.Операция.Наименование КАК Операция
ИЗ
    Документ.ВыполнениеРабот.Операции КАК ВыполнениеРаботОперации
ГДЕ
    ВыполнениеРаботОперации.Операция.Наименование = "Фасовка"
    И НЕ ВыполнениеРаботОперации.Операция.Наименование = "Отправка"

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

Почему прямой запрос не дает нужного результата?

Прежде всего, давайте проанализируем, почему наш первый запрос (как в сообщении 4) не работает корректно. Условие ГДЕ ВыполнениеРаботОперации.Операция.Наименование = "Фасовка" И НЕ ВыполнениеРаботОперации.Операция.Наименование = "Отправка" проверяется для каждой отдельной строки табличной части документа. Рассмотрим пример:

  1. Документ №1 имеет строки: "Фасовка", "Упаковка".
  2. Документ №2 имеет строки: "Фасовка", "Отправка".
  3. Документ №3 имеет строки: "Отправка", "Загрузка".

Если мы применяем исходный запрос:

Проблема заключается в том, что условие И НЕ Операция.Наименование = "Отправка" проверяет, что *эта конкретная* строка не является "Отправкой", а не то, что *весь документ* не содержит "Отправку". Наша задача состоит в том, чтобы отобрать документы целиком, а не отдельные строки их табличных частей.

Решение 1: Отбор документов с помощью подзапросов (В и НЕ В)

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

Принцип работы

  1. Находим документы с "Фасовкой": Мы формируем первый подзапрос, который выбирает уникальные ссылки на все документы, в табличной части которых присутствует операция "Фасовка".
  2. Находим документы с "Отправкой": Затем мы формируем второй подзапрос, который аналогично выбирает уникальные ссылки на все документы, в табличной части которых присутствует операция "Отправка".
  3. Исключаем лишнее: В основном запросе мы выбираем документы из первого списка (где есть "Фасовка") и с помощью оператора НЕ В исключаем из них все документы, которые присутствуют во втором списке (где есть "Отправка").

Пример кода

Предположим, что наша табличная часть называется Операции, а реквизит табличной части, хранящий наименование операции, это ссылка на справочник, например Операция, и мы сравниваем ее поле Наименование.


ВЫБРАТЬ РАЗЛИЧНЫЕ
    ТабЧастьДокумента.Ссылка КАК Ссылка
ИЗ
    Документ.ВыполнениеРабот.Операции КАК ТабЧастьДокумента
ГДЕ
    ТабЧастьДокумента.Ссылка В
        (ВЫБРАТЬ РАЗЛИЧНЫЕ
            СтрокаОперации.Ссылка
        ИЗ
            Документ.ВыполнениеРабот.Операции КАК СтрокаОперации
        ГДЕ
            СтрокаОперации.Операция.Наименование = "Фасовка")
    И ТабЧастьДокумента.Ссылка НЕ В
        (ВЫБРАТЬ РАЗЛИЧНЫЕ
            СтрокаОперации.Ссылка
        ИЗ
            Документ.ВыполнениеРабот.Операции КАК СтрокаОперации
        ГДЕ
            СтрокаОперации.Операция.Наименование = "Отправка")

Подробное объяснение запроса

Давайте подробно рассмотрим каждую часть этого запроса:

  1. ВЫБРАТЬ РАЗЛИЧНЫЕ ТабЧастьДокумента.Ссылка КАК Ссылка

    Это основная секция выбора. Мы хотим получить уникальные ссылки на документы, поэтому используем РАЗЛИЧНЫЕ. Алиас ТабЧастьДокумента используется для удобства обращения к табличной части в основном запросе.

  2. ИЗ Документ.ВыполнениеРабот.Операции КАК ТабЧастьДокумента

    Мы указываем, что нашим основным источником данных является табличная часть Операции документа ВыполнениеРабот. Для получения ссылки на родительский документ мы используем поле Ссылка из табличной части.

  3. ГДЕ ТабЧастьДокумента.Ссылка В (...) (Первый подзапрос)

    Эта часть отбирает ссылки на документы, которые содержат операцию "Фасовка".

    
    (ВЫБРАТЬ РАЗЛИЧНЫЕ
        СтрокаОперации.Ссылка
    ИЗ
        Документ.ВыполнениеРабот.Операции КАК СтрокаОперации
    ГДЕ
        СтрокаОперации.Операция.Наименование = "Фасовка")
    

    Здесь мы:

    • Внутри подзапроса снова обращаемся к табличной части Документ.ВыполнениеРабот.Операции, но даем ей другой алиас (СтрокаОперации), чтобы избежать путаницы.
    • Фильтруем строки по условию СтрокаОперации.Операция.Наименование = "Фасовка".
    • Получаем уникальные ссылки на документы, которые имеют хотя бы одну строку с "Фасовкой".
  4. И ТабЧастьДокумента.Ссылка НЕ В (...) (Второй подзапрос)

    Эта часть отфильтровывает документы, которые содержат операцию "Отправка".

    
    (ВЫБРАТЬ РАЗЛИЧНЫЕ
        СтрокаОперации.Ссылка
    ИЗ
        Документ.ВыполнениеРабот.Операции КАК СтрокаОперации
    ГДЕ
        СтрокаОперации.Операция.Наименование = "Отправка")
    

    Аналогично первому подзапросу, мы получаем уникальные ссылки на документы, которые имеют хотя бы одну строку с "Отправкой". Затем основной запрос использует оператор НЕ В, чтобы исключить эти документы из конечного результата.

Таким образом, мы сначала собираем "кандидатов" (все документы с "Фасовкой"), а затем "дисквалифицируем" тех, у кого есть "Отправка", получая в итоге нужный список.

Решение 2: Отбор документов с помощью агрегатных функций и условия ИМЕЮЩИЕ

Второй эффективный способ решения нашей задачи – использование агрегатных функций в сочетании с условием ИМЕЮЩИЕ. Этот подход часто бывает более производительным для сложных условий с агрегацией, так как он обрабатывает данные один раз, группируя их.

Принцип работы

Идея этого метода заключается в присвоении "баллов" каждой операции в табличной части, а затем суммировании этих баллов для каждого документа. Если итоговая сумма баллов документа удовлетворяет нашему условию (например, больше нуля), то документ подходит.

  1. Группируем по документу: Мы объединяем все строки табличной части по ссылке на родительский документ.
  2. Присваиваем "оценки" операциям: Для каждой операции в табличной части мы назначаем числовое значение:
    • Если операция "Фасовка" – положительное число (например, 1).
    • Если операция "Отправка" – большое отрицательное число (например, -99999).
    • Для всех остальных операций – 0.
  3. Суммируем "оценки": Для каждого документа мы суммируем все "оценки" его операций.
  4. Фильтруем по сумме: С помощью условия ИМЕЮЩИЕ мы отбираем только те документы, у которых итоговая сумма больше нуля. Большое отрицательное число для "Отправки" гарантирует, что даже если в документе есть много "Фасовок", но есть хотя бы одна "Отправка", сумма все равно будет отрицательной, и документ будет исключен.

Пример кода


ВЫБРАТЬ
    ВыполнениеРаботОперации.Ссылка КАК Ссылка
ИЗ
    Документ.ВыполнениеРабот.Операции КАК ВыполнениеРаботОперации
СГРУППИРОВАТЬ ПО
    ВыполнениеРаботОперации.Ссылка
ИМЕЮЩИЕ
    СУММА(ВЫБОР
        КОГДА ВыполнениеРаботОперации.Операция.Наименование = "Фасовка"
            ТОГДА 1
        КОГДА ВыполнениеРаботОперации.Операция.Наименование = "Отправка"
            ТОГДА -99999
        ИНАЧЕ 0
    КОНЕЦ) > 0

Подробное объяснение запроса

Давайте разберем этот запрос шаг за шагом:

  1. ВЫБРАТЬ ВыполнениеРаботОперации.Ссылка КАК Ссылка

    В секции ВЫБРАТЬ мы указываем, что хотим получить ссылки на документы.

  2. ИЗ Документ.ВыполнениеРабот.Операции КАК ВыполнениеРаботОперации

    Источником данных, как и ранее, является табличная часть документа ВыполнениеРабот.

  3. СГРУППИРОВАТЬ ПО ВыполнениеРаботОперации.Ссылка

    Это ключевая часть. Мы группируем все строки табличной части по родительскому документу. Таким образом, все операции одного документа собираются вместе, и для них можно вычислить агрегатные функции.

  4. ИМЕЮЩИЕ СУММА(ВЫБОР КОГДА ... ТОГДА ... ИНАЧЕ 0 КОНЕЦ) > 0

    Это условие применяется после группировки и вычисления агрегатных функций для каждой группы (то есть для каждого документа). Рассмотрим внутренний ВЫБОР:

    
    ВЫБОР
        КОГДА ВыполнениеРаботОперации.Операция.Наименование = "Фасовка"
            ТОГДА 1
        КОГДА ВыполнениеРаботОперации.Операция.Наименование = "Отправка"
            ТОГДА -99999
        ИНАЧЕ 0
    КОНЕЦ
    
    • Для каждой строки табличной части, если ее операция — "Фасовка", она получает +1 балл.
    • Если операция — "Отправка", она получает -99999 баллов.
    • Все остальные операции приносят 0 баллов.

    Затем функция СУММА складывает эти баллы для всех строк внутри каждой группы (каждого документа).

    • Если в документе есть "Фасовка" и нет "Отправки", сумма будет положительной (например, 1, 2, 3 и т.д.).
    • Если в документе есть "Отправка", значение -99999 "перевесит" любое количество "Фасовок", и сумма станет отрицательной, исключая документ из выборки.
    • Если нет ни "Фасовки", ни "Отправки", или только "Отправка", сумма будет <= 0.

    В итоге, ИМЕЮЩИЕ СУММА(...) > 0 отбирает только те документы, которые содержат "Фасовку" и гарантированно не содержат "Отправку".

Общие рекомендации по работе с табличными частями в запросах 1С

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

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

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

← На главную