В управленческом учете часто возникает задача проанализировать «пустые» зоны: понять, какие клиенты перестали совершать покупки или какие товары из ассортимента лежат мертвым грузом. Стандартные отчеты по продажам обычно строятся по регистру ВыручкаИСебестоимостьПродаж и показывают только те записи, где движение уже произошло. Если продажи не было — записи в регистре нет, и строка в отчет не попадает.
Разберем подробно, как спроектировать запрос, который позволит вывести полную картину: и реальные продажи, и тех контрагентов или товары, по которым движений за период не зафиксировано.
Проанализируем ситуацию: менеджеру нужно видеть отчет, где по строкам идут все клиенты, по колонкам — все ключевые товары, а на пересечении — количество продаж. Если мы просто сделаем ЛЕВОЕ СОЕДИНЕНИЕ справочника Партнеры с регистром продаж, мы увидим всех клиентов, но только по тем товарам, которые они купили.
Чтобы увидеть, что клиент не купил конкретный товар (например, арбузы), нам нужно сначала создать «сетку» из всех возможных комбинаций клиентов и товаров. В языке запросов 1С это достигается соединением двух справочников без указания условия связи (декартово произведение).
Рассмотрим пример кода для создания такой временной таблицы:
ВЫБРАТЬ
Номенклатура.Ссылка КАК Номенклатура,
Партнеры.Ссылка КАК Партнер
ПОМЕСТИТЬ ВТ_ПолнаяМатрица
ИЗ
Справочник.Номенклатура КАК Номенклатура
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Партнеры КАК Партнеры
ПО (ИСТИНА)
ГДЕ
НЕ Номенклатура.ЭтоГруппа
И НЕ Партнеры.ЭтоГруппа
;
Важный нюанс: использование такого метода на больших базах (десятки тысяч товаров и клиентов) может привести к формированию таблицы из миллиардов строк, что «повесит» сервер. Поэтому всегда используйте фильтры (например, по конкретной группе номенклатуры или сегменту партнеров) во второй части запроса ГДЕ.
После того как мы получили временную таблицу ВТ_ПолнаяМатрица, мы можем соединить её с реальными данными из регистра. Выясним причину отсутствия данных с помощью функции ЕСТЬNULL.
Проанализируем следующий запрос:
ВЫБРАТЬ
Матрица.Партнер КАК Партнер,
Матрица.Номенклатура КАК Номенклатура,
ЕСТЬNULL(Продажи.КоличествоОборот, 0) КАК Количество,
ЕСТЬNULL(Продажи.СуммаВыручкиОборот, 0) КАК Выручка
ИЗ
ВТ_ПолнаяМатрица КАК Матрица
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ВыручкаИСебестоимостьПродаж.Обороты(&НачалоПериода, &КонецПериода, , ) КАК Продажи
ПО Матрица.Партнер = Продажи.АналитикаУчетаПоПартнерам.Партнер
И Матрица.Номенклатура = Продажи.АналитикаУчетаНоменклатуры.Номенклатура
В данном случае, если продажи не было, поле Количество примет значение 0, и запись не исчезнет из отчета. Это позволит менеджеру увидеть наглядный список: Клиент «А» — Товар «Арбуз» — 0 штук.
Если задача стоит шире и нужно просто дополнить отчет данными о тех, кто не покупал (без жесткой привязки к каждому товару), мы можем воспользоваться возможностями СКД. Рассмотрим вариант с объединением двух наборов данных.
ВыручкаИСебестоимостьПродаж.Партнеры.Партнер.Такой подход позволит системе автоматически выводить всех партнеров, даже если по ним нет записей в первом наборе данных (продажах). В настройках отчета достаточно будет установить отбор Количество ЕСТЬ NULL или Количество = 0, чтобы получить список «молчунов».
Как мы уже выяснили, декартово произведение N * M — это опасная операция. Рассмотрим, как профессиональные разработчики оптимизируют такие задачи:
ВТ_Клиенты, затем нужные товары в ВТ_Товары, и только потом соединяйте их. Это кратно уменьшает нагрузку на SQL-сервер.&НачалоПериода и &КонецПериода непосредственно в параметры таблицы, а не в условие ГДЕ.Посмотрим на пример оптимизированного выбора партнеров:
ВЫБРАТЬ
Партнеры.Ссылка КАК Партнер
ПОМЕСТИТЬ ВТ_АктивныеКлиенты
ИЗ
Справочник.Партнеры КАК Партнеры
ГДЕ
Партнеры.ПометкаУдаления = ЛОЖЬ
И Партнеры.Клиент = ИСТИНА
И Партнеры.ОсновнойМенеджер = &ТекущийМенеджер
;
Помимо простого вывода нулей, такие отчеты являются базой для RFM-анализа (Recency, Frequency, Monetary). Рассмотрим подробнее: строя отчет по клиентам, которые не покупали, вы фактически анализируете показатель Recency (давность сделки). Если клиент не покупал более 30 дней, он попадает в зону риска.
Также подобные запросы незаменимы для контроля Матрицы ассортимента. Например, в торговой сети магазины определенного формата обязаны продавать 50 видов хлеба. Отчет «все товары — все магазины» с левым соединением продаж сразу покажет, в каких торговых точках нарушена товарная матрица.
Подводя итог, можно сказать, что решение задачи «продаж с нулевыми остатками» — это всегда баланс между полнотой данных и производительностью системы. Используйте ЛЕВОЕ СОЕДИНЕНИЕ от справочников к регистрам и не забывайте про фильтрацию данных во временных таблицах.