В процессе работы с 1С часто возникает задача отобрать документы, например, «Заказы покупателя», которые содержат не просто какой-либо товар из списка, а полный и обязательный набор этих товаров. Стандартная конструкция запроса ГДЕ Номенклатура В (&СписокНоменклатуры) здесь не подходит, так как она сработает по логике «ИЛИ» и вернет все заказы, где есть хотя бы одна позиция из списка.
В этой статье мы подробно разберем, как правильно составить запрос, чтобы он отбирал документы, в которых присутствуют все указанные товары. А также проанализируем интересную ситуацию, когда стандартный отчет после обновления платформы перестал работать с подобным отбором.
ИМЕЮЩИЕЭто самый классический и производительный способ решения данной задачи в языке запросов 1С. Логика этого метода строится на подсчете уникальных позиций из нашего списка в каждом документе. Если это количество совпадает с размером нашего исходного списка, значит, документ нам подходит.
Разберем алгоритм по шагам:
ГДЕ мы отбираем все табличные части документов, строки которых содержат хотя бы один из искомых товаров. Это позволяет значительно сократить объем данных для дальнейшей обработки. Для этого как раз и используется оператор В (...).СГРУППИРОВАТЬ ПО Ссылка). Таким образом, для каждого документа у нас будет своя группа записей, содержащая строки с товарами из нашего списка.ИМЕЮЩИЕ (аналог HAVING в SQL), которая позволяет фильтровать уже сгруппированные данные.ИМЕЮЩИЕ мы с помощью агрегатной функции КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Номенклатура) подсчитываем, сколько уникальных товаров из нашего списка нашлось в каждом конкретном документе. Затем мы сравниваем это число с общим количеством товаров в переданном списке (это значение нужно передать в запрос отдельным параметром). Если количества равны, значит, в документе найдены все искомые позиции.Посмотрим на пример такого запроса. Предположим, у нас есть параметр &Список, в который передан список нужных номенклатурных позиций, и параметр &СписокКоличество, содержащий количество элементов в этом списке.
ВЫБРАТЬ РАЗЛИЧНЫЕ
Т.Ссылка КАК Ссылка
ИЗ
Документ.ЗаказПокупателя.Товары КАК Т
ГДЕ
Т.Номенклатура В(&Список)
СГРУППИРОВАТЬ ПО
Т.Ссылка
ИМЕЮЩИЕ
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Т.Номенклатура) = &СписокКоличество
Важный нюанс: такой запрос отберет документы, в которых есть все товары из списка &Список, но при этом могут быть и другие, дополнительные товары. Если стоит задача найти документы, где содержатся только товары из списка и никаких больше, запрос нужно будет усложнить, добавив проверку на общее количество позиций в документе.
Этот подход выполняет ту же задачу, но может быть более читаемым и структурированным, особенно в составе больших и сложных запросов. Идея состоит в том, чтобы сначала поместить наш список искомых товаров во временную таблицу, а затем соединить с ней табличную часть документов.
Рассмотрим по шагам:
ВТСписокНоменклатуры) и помещаем в нее номенклатуру из переданного параметра (например, &ТаблицаЗначенийНоменклатуры).ВНУТРЕННЕЕ СОЕДИНЕНИЕ (INNER JOIN) табличной части документов ЗаказПокупателя.Товары с нашей временной таблицей. Соединение производится по полю номенклатуры. В результате этого соединения в выборке останутся только те строки заказов, номенклатура в которых присутствует в нашем списке.ИМЕЮЩИЕ проверяем, что количество найденных уникальных позиций равно общему количеству искомых товаров.Пример реализации с временной таблицей:
// Пакет 1: Помещаем список номенклатуры во временную таблицу
ВЫБРАТЬ
НоменклатураТаблица.Номенклатура КАК Номенклатура
ПОМЕСТИТЬ ВТСписокНоменклатуры
ИЗ
&ТаблицаЗначенийНоменклатуры КАК НоменклатураТаблица
;
////////////////////////////////////////////////////////////////////////////////
// Пакет 2: Основной запрос для отбора документов
ВЫБРАТЬ
ЗаказПокупателяТовары.Ссылка КАК Заказ
ИЗ
Документ.ЗаказПокупателя.Товары КАК ЗаказПокупателяТовары
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТСписокНоменклатуры
ПО ЗаказПокупателяТовары.Номенклатура = ВТСписокНоменклатуры.Номенклатура
СГРУППИРОВАТЬ ПО
ЗаказПокупателяТовары.Ссылка
ИМЕЮЩИЕ
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ЗаказПокупателяТовары.Номенклатура) = &КоличествоНоменклатуры
Этот метод не дает существенного выигрыша в производительности для этой конкретной задачи, но делает логику запроса более прозрачной и может быть предпочтительнее при работе с большим количеством соединений и условий.
В ходе обсуждения автор столкнулся с очень любопытной ситуацией: ранее в стандартном отчете можно было задать несколько условий отбора вида ХарактеристикаНоменклатуры = "А", ХарактеристикаНоменклатуры = "Б", и отчет корректно находил заказы, где есть и товар "А", и товар "Б". А после обновления это перестало работать.
На первый взгляд, такая конструкция нелогична. Условие Поле = "А" И Поле = "Б" для одной и той же записи никогда не может быть истинным, так как поле не может одновременно иметь два разных значения. Так почему же это работало раньше?
Выясним причину. Ответ кроется в поведении Системы Компоновки Данных (СКД) и режиме совместимости платформы 1С.
...ГДЕ Поле = "А" И Поле = "Б", что, естественно, не возвращает никаких данных.Решение, которое нашел автор, — установить для конфигурации режим совместимости с версией 8.3.10 — является рабочим, но временным обходным маневром. Оно заставляет платформу эмулировать поведение старой версии, но при этом лишает вас возможности использовать новые функции и оптимизации более свежих версий платформы.
Правильный путь в долгосрочной перспективе — это не полагаться на скрытые особенности конкретных версий, а использовать стандартные и надежные конструкции языка запросов, такие как методы с ИМЕЮЩИЕ, описанные выше. При доработке или создании новых отчетов всегда стоит отдавать предпочтение именно им.