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