Приветствуем вас! Сегодня мы рассмотрим распространенную задачу в мире 1С: как правильно составить запрос, чтобы отобрать документы, в табличной части которых одновременно присутствует определенная операция (например, "Фасовка"), но при этом полностью отсутствует другая операция (например, "Отправка"). Эта задача часто вызывает затруднения, поскольку прямое использование условий в секции ГДЕ может привести к неожиданным результатам. Давайте разберем эту проблему и предложим несколько эффективных решений.
Изначальная попытка, как правило, выглядит следующим образом:
ВЫБРАТЬ
ВыполнениеРаботОперации.Ссылка КАК Ссылка,
ВыполнениеРаботОперации.Операция.Наименование КАК Операция
ИЗ
Документ.ВыполнениеРабот.Операции КАК ВыполнениеРаботОперации
ГДЕ
ВыполнениеРаботОперации.Операция.Наименование = "Фасовка"
И НЕ ВыполнениеРаботОперации.Операция.Наименование = "Отправка"
Мы выясним, почему такой запрос не работает так, как мы ожидаем, и предложим два надежных способа для получения правильного результата.
Прежде всего, давайте проанализируем, почему наш первый запрос (как в сообщении 4) не работает корректно. Условие ГДЕ ВыполнениеРаботОперации.Операция.Наименование = "Фасовка" И НЕ ВыполнениеРаботОперации.Операция.Наименование = "Отправка" проверяется для каждой отдельной строки табличной части документа. Рассмотрим пример:
Если мы применяем исходный запрос:
Проблема заключается в том, что условие И НЕ Операция.Наименование = "Отправка" проверяет, что *эта конкретная* строка не является "Отправкой", а не то, что *весь документ* не содержит "Отправку". Наша задача состоит в том, чтобы отобрать документы целиком, а не отдельные строки их табличных частей.
Один из самых надежных и понятных способов решения этой задачи – использовать подзапросы с операторами В и НЕ В. Этот подход позволяет нам сначала отобрать все документы, содержащие нужную операцию, а затем исключить из них те, которые содержат запрещенную операцию. Давайте разберем его по шагам.
НЕ В исключаем из них все документы, которые присутствуют во втором списке (где есть "Отправка").Предположим, что наша табличная часть называется Операции, а реквизит табличной части, хранящий наименование операции, это ссылка на справочник, например Операция, и мы сравниваем ее поле Наименование.
ВЫБРАТЬ РАЗЛИЧНЫЕ
ТабЧастьДокумента.Ссылка КАК Ссылка
ИЗ
Документ.ВыполнениеРабот.Операции КАК ТабЧастьДокумента
ГДЕ
ТабЧастьДокумента.Ссылка В
(ВЫБРАТЬ РАЗЛИЧНЫЕ
СтрокаОперации.Ссылка
ИЗ
Документ.ВыполнениеРабот.Операции КАК СтрокаОперации
ГДЕ
СтрокаОперации.Операция.Наименование = "Фасовка")
И ТабЧастьДокумента.Ссылка НЕ В
(ВЫБРАТЬ РАЗЛИЧНЫЕ
СтрокаОперации.Ссылка
ИЗ
Документ.ВыполнениеРабот.Операции КАК СтрокаОперации
ГДЕ
СтрокаОперации.Операция.Наименование = "Отправка")
Давайте подробно рассмотрим каждую часть этого запроса:
ВЫБРАТЬ РАЗЛИЧНЫЕ ТабЧастьДокумента.Ссылка КАК Ссылка
Это основная секция выбора. Мы хотим получить уникальные ссылки на документы, поэтому используем РАЗЛИЧНЫЕ. Алиас ТабЧастьДокумента используется для удобства обращения к табличной части в основном запросе.
ИЗ Документ.ВыполнениеРабот.Операции КАК ТабЧастьДокумента
Мы указываем, что нашим основным источником данных является табличная часть Операции документа ВыполнениеРабот. Для получения ссылки на родительский документ мы используем поле Ссылка из табличной части.
ГДЕ ТабЧастьДокумента.Ссылка В (...) (Первый подзапрос)
Эта часть отбирает ссылки на документы, которые содержат операцию "Фасовка".
(ВЫБРАТЬ РАЗЛИЧНЫЕ
СтрокаОперации.Ссылка
ИЗ
Документ.ВыполнениеРабот.Операции КАК СтрокаОперации
ГДЕ
СтрокаОперации.Операция.Наименование = "Фасовка")
Здесь мы:
Документ.ВыполнениеРабот.Операции, но даем ей другой алиас (СтрокаОперации), чтобы избежать путаницы.СтрокаОперации.Операция.Наименование = "Фасовка".И ТабЧастьДокумента.Ссылка НЕ В (...) (Второй подзапрос)
Эта часть отфильтровывает документы, которые содержат операцию "Отправка".
(ВЫБРАТЬ РАЗЛИЧНЫЕ
СтрокаОперации.Ссылка
ИЗ
Документ.ВыполнениеРабот.Операции КАК СтрокаОперации
ГДЕ
СтрокаОперации.Операция.Наименование = "Отправка")
Аналогично первому подзапросу, мы получаем уникальные ссылки на документы, которые имеют хотя бы одну строку с "Отправкой". Затем основной запрос использует оператор НЕ В, чтобы исключить эти документы из конечного результата.
Таким образом, мы сначала собираем "кандидатов" (все документы с "Фасовкой"), а затем "дисквалифицируем" тех, у кого есть "Отправка", получая в итоге нужный список.
Второй эффективный способ решения нашей задачи – использование агрегатных функций в сочетании с условием ИМЕЮЩИЕ. Этот подход часто бывает более производительным для сложных условий с агрегацией, так как он обрабатывает данные один раз, группируя их.
Идея этого метода заключается в присвоении "баллов" каждой операции в табличной части, а затем суммировании этих баллов для каждого документа. Если итоговая сумма баллов документа удовлетворяет нашему условию (например, больше нуля), то документ подходит.
ИМЕЮЩИЕ мы отбираем только те документы, у которых итоговая сумма больше нуля. Большое отрицательное число для "Отправки" гарантирует, что даже если в документе есть много "Фасовок", но есть хотя бы одна "Отправка", сумма все равно будет отрицательной, и документ будет исключен.
ВЫБРАТЬ
ВыполнениеРаботОперации.Ссылка КАК Ссылка
ИЗ
Документ.ВыполнениеРабот.Операции КАК ВыполнениеРаботОперации
СГРУППИРОВАТЬ ПО
ВыполнениеРаботОперации.Ссылка
ИМЕЮЩИЕ
СУММА(ВЫБОР
КОГДА ВыполнениеРаботОперации.Операция.Наименование = "Фасовка"
ТОГДА 1
КОГДА ВыполнениеРаботОперации.Операция.Наименование = "Отправка"
ТОГДА -99999
ИНАЧЕ 0
КОНЕЦ) > 0
Давайте разберем этот запрос шаг за шагом:
ВЫБРАТЬ ВыполнениеРаботОперации.Ссылка КАК Ссылка
В секции ВЫБРАТЬ мы указываем, что хотим получить ссылки на документы.
ИЗ Документ.ВыполнениеРабот.Операции КАК ВыполнениеРаботОперации
Источником данных, как и ранее, является табличная часть документа ВыполнениеРабот.
СГРУППИРОВАТЬ ПО ВыполнениеРаботОперации.Ссылка
Это ключевая часть. Мы группируем все строки табличной части по родительскому документу. Таким образом, все операции одного документа собираются вместе, и для них можно вычислить агрегатные функции.
ИМЕЮЩИЕ СУММА(ВЫБОР КОГДА ... ТОГДА ... ИНАЧЕ 0 КОНЕЦ) > 0
Это условие применяется после группировки и вычисления агрегатных функций для каждой группы (то есть для каждого документа). Рассмотрим внутренний ВЫБОР:
ВЫБОР
КОГДА ВыполнениеРаботОперации.Операция.Наименование = "Фасовка"
ТОГДА 1
КОГДА ВыполнениеРаботОперации.Операция.Наименование = "Отправка"
ТОГДА -99999
ИНАЧЕ 0
КОНЕЦ
Затем функция СУММА складывает эти баллы для всех строк внутри каждой группы (каждого документа).
В итоге, ИМЕЮЩИЕ СУММА(...) > 0 отбирает только те документы, которые содержат "Фасовку" и гарантированно не содержат "Отправку".
Для эффективной работы с запросами в 1С, особенно когда речь идет о табличных частях, рекомендуем учитывать следующие моменты:
Документ.ВыполнениеРабот.Операции.Ссылка табличной части. Это позволяет связывать данные табличной части с данными шапки документа (например, ВыполнениеРаботОперации.Ссылка.Дата).Период для регистров, чтобы сузить объем обрабатываемых данных. Это значительно повышает производительность запросов.Мы рассмотрели два мощных и эффективных способа для решения задачи отбора документов по сложным условиям в их табличных частях. Метод с подзапросами В / НЕ В более декларативен и понятен для начинающих, а подход с агрегатными функциями и ИМЕЮЩИЕ часто бывает более производительным и изящным для опытных разработчиков. Выбирайте тот, который лучше подходит для вашей конкретной ситуации и предпочтений в написании кода.