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