В практике работы программиста 1С часто возникает задача анализа «упущенных возможностей». Одной из таких задач является формирование отчета по товарам, которые клиент не приобрел за определенный период времени, хотя эти товары были в наличии или входят в регулярный ассортимент. Это позволяет отделу продаж предлагать клиенту новые позиции и расширять его «корзину».
В данной статье мы подробно разберем, как реализовать такой отчет на языке запросов 1С, особенно для конфигураций на обычных формах, таких как «Управление торговлей 10.3», и рассмотрим нюансы оптимизации производительности при работе с большими объемами данных.
Рассмотрим ситуацию: у нас есть полный список товаров (например, 100 наименований на складе) и есть данные о продажах конкретному контрагенту (например, он купил только 20 наименований). Наша цель — получить список из оставшихся 80 товаров.
С точки зрения теории множеств, нам нужно найти разность между множеством всех доступных товаров и множеством товаров, купленных клиентом. В языке запросов 1С это реализуется через механизм ЛЕВОЕ СОЕДИНЕНИЕ двух таблиц по полю Номенклатура с последующей фильтрацией тех записей, для которых не нашлось соответствия в правой таблице.
Прежде всего, нам нужно решить, что мы считаем «всеми товарами». Рассмотрим три варианта:
Проанализируем пример, где мы берем товары, бывшие в наличии. Для этого используем виртуальную таблицу остатков:
ВЫБРАТЬ РАЗЛИЧНЫЕ
ТоварыНаСкладахОстатки.Номенклатура КАК Номенклатура
ПОМЕСТИТЬ ВТ_ВсеТовары
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки(&ДатаОстатков, ) КАК ТоварыНаСкладахОстатки
ГДЕ
ТоварыНаСкладахОстатки.КоличествоОстаток > 0
;
Теперь нам нужно получить список того, что конкретный клиент уже купил за интересующий нас период. В конфигурации УТ 10.3 для этого лучше всего подходит регистр накопления Продажи.
ВЫБРАТЬ РАЗЛИЧНЫЕ
ПродажиОбороты.Номенклатура КАК Номенклатура
ПОМЕСТИТЬ ВТ_КупленныеТовары
ИЗ
РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, , Контрагент = &ВыбранныйКлиент) КАК ПродажиОбороты
;
Это самый важный этап. Мы берем таблицу ВТ_ВсеТовары и присоединяем к ней ВТ_КупленныеТовары. Те товары, которые клиент не покупал, будут иметь значение NULL в колонках правой таблицы.
Разберем готовый код запроса:
ВЫБРАТЬ
ВсеТовары.Номенклатура КАК ТоварКоторыйНеКупили
ИЗ
ВТ_ВсеТовары КАК ВсеТовары
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_КупленныеТовары КАК КупленныеТовары
ПО ВсеТовары.Номенклатура = КупленныеТовары.Номенклатура
ГДЕ
КупленныеТовары.Номенклатура ЕСТЬ NULL
Почему это работает? Условие ЕСТЬ NULL отсекает все строки, где произошло успешное соединение (то есть те товары, которые клиент купил). Остаются только те позиции из левой таблицы, для которых в правой таблице (продажах) записей не обнаружено.
Если задача усложняется и нам нужно построить матрицу по всем клиентам (какой клиент что не купил), обычного левого соединения будет недостаточно. Нам понадобится создать таблицу всех возможных комбинаций пар «Клиент — Номенклатура».
Рассмотрим, как это реализовать:
ВЫБРАТЬ
ВыбранныеКонтрагенты.Ссылка КАК Контрагент,
ВыбраннаяНоменклатура.Ссылка КАК Номенклатура
ПОМЕСТИТЬ ВТ_Матрица
ИЗ
Справочник.Контрагенты КАК ВыбранныеКонтрагенты
СОЕДИНЕНИЕ Справочник.Номенклатура КАК ВыбраннаяНоменклатура
ПО (ИСТИНА) // Перекрестное соединение
ГДЕ
НЕ ВыбранныеКонтрагенты.ЭтоГруппа
И НЕ ВыбраннаяНоменклатура.ЭтоГруппа;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Матрица.Контрагент,
Матрица.Номенклатура
ИЗ
ВТ_Матрица КАК Матрица
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.Продажи.Обороты(&Нач, &Кон, , ) КАК Продажи
ПО Матрица.Контрагент = Продажи.Контрагент
И Матрица.Номенклатура = Продажи.Номенклатура
ГДЕ
Продажи.Номенклатура ЕСТЬ NULL
Важное замечание по производительности: Будьте осторожны с перекрестным соединением. Если в базе 1 000 клиентов и 10 000 товаров, временная таблица составит 10 000 000 записей. Всегда используйте жесткие фильтры по группам номенклатуры или конкретным сегментам клиентов.
В современных конфигурациях (да и в УТ 10.3 при использовании СКД) такие отчеты можно строить еще элегантнее. Вы можете создать два набора данных: один с полным списком товаров, другой — с продажами. В настройках связей наборов данных можно указать «Левое соединение» и в настройках самого отчета вывести только те записи, где количество из набора продаж НЕ ЗАПОЛНЕНО.
Проанализируем преимущества использования Временных таблиц в таких задачах:
Номенклатура и Контрагент, что значительно ускорит итоговое соединение.При построении подобных отчетов в «1С:Управление торговлей 10.3», обратите внимание на следующие моменты:
Номенклатура и ХарактеристикаНоменклатуры.ПометкаУдаления = ЛОЖЬ).ВидНоменклатуры = ЗНАЧЕНИЕ(Перечисление.ВидыНоменклатуры.Товар).Таким образом, мы выяснили, что ключ к решению задачи — правильное использование ЛЕВОГО СОЕДИНЕНИЯ с фильтрацией по NULL. Этот метод универсален и подходит для поиска любых «отсутствующих» данных в системе.