При разработке управленческих отчетов программисты часто сталкиваются с необходимостью сопоставить данные из разных регистров за разные периоды. Типичный пример: нужно увидеть, сколько товара продавалось в прошлом и позапрошлом годах, и сравнить эти цифры с текущими остатками на складах. Основная сложность заключается в том, что товар может присутствовать на остатках склада, с которого никогда не было продаж, или наоборот — продажи были, а остатка сейчас нет. В этой статье мы подробно разберем, как правильно спроектировать запрос, чтобы данные не дублировались и не терялись.
Рассмотрим ситуацию, с которой начинают многие: попытка связать таблицы продаж и остатков через ЛЕВОЕ СОЕДИНЕНИЕ или ПОЛНОЕ СОЕДИНЕНИЕ. Проанализируем, почему этот подход часто приводит к ошибкам. Если мы соединяем продажи за прошлый год с продажами за позапрошлый год по полям Номенклатура и Склад, а затем пытаемся «приклеить» к ним таблицу остатков, возникают следующие риски:
Склады (как основным), если продажи осуществлялись по складу, которого нет в выборке (или наоборот), данные могут «уплыть» в пустые строки.ИЛИ в условиях соединения (например, ПО ВТ1.Склад = ВТ4.Склад ИЛИ ВТ2.Склад = ВТ4.Склад) крайне негативно сказывается на производительности и часто выдает непредсказуемый результат в виде декартова произведения строк.Чтобы избежать этих проблем, проанализируем более надежный механизм — объединение таблиц.
Наиболее правильным и производительным способом решения данной задачи в 1С является использование конструкции ОБЪЕДИНИТЬ ВСЕ (UNION ALL). Суть метода заключается в том, чтобы сначала собрать все данные в одну «плоскую» временную таблицу, а затем сгруппировать их. Выясним, как это реализовать по шагам.
Сначала подготовим данные по продажам за первый период (прошлый год). Мы используем виртуальную таблицу Обороты регистра накопления Продажи. Обратите внимание, что мы сразу создаем пустые колонки для тех данных, которые появятся в других частях запроса.
ВЫБРАТЬ
ПродажиОбороты.Номенклатура КАК Номенклатура,
ПродажиОбороты.Склад КАК Склад,
ПродажиОбороты.КоличествоОборот КАК ПродажиПрошлыйГод,
0 КАК ПродажиПозапрошлыйГод,
0 КАК Остаток
ПОМЕСТИТЬ ВТ_Данные
ИЗ
РегистрНакопления.Продажи.Обороты(&НачалоПрошлогоГода, &КонецПрошлогоГода, , ) КАК ПродажиОбороты
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
ПродажиОбороты.Номенклатура,
ПродажиОбороты.Склад,
0,
ПродажиОбороты.КоличествоОборот,
0
ИЗ
РегистрНакопления.Продажи.Обороты(&НачалоПозапрошлогоГода, &КонецПозапрошлогоГода, , ) КАК ПродажиОбороты
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
ТоварыНаСкладахОстатки.Номенклатура,
ТоварыНаСкладахОстатки.Склад,
0,
0,
ТоварыНаСкладахОстатки.КоличествоОстаток
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки(&ДатаОстатков, ) КАК ТоварыНаСкладахОстатки
В данном примере мы собрали три разных потока данных в одну временную таблицу ВТ_Данные. Каждая запись несет информацию либо только о продажах одного года, либо только о продажах другого, либо только об остатках. При этом колонки «чужих» ресурсов заполнены нулями.
Теперь нам необходимо «схлопнуть» эти записи, чтобы для каждой пары Номенклатура + Склад была только одна итоговая строка. Рассмотрим подробнее финальный запрос:
ВЫБРАТЬ
Т.Номенклатура КАК Номенклатура,
Т.Склад КАК Склад,
СУММА(Т.ПродажиПрошлыйГод) КАК ПродажиПрошлыйГод,
СУММА(Т.ПродажиПозапрошлыйГод) КАК ПродажиПозапрошлыйГод,
СУММА(Т.Остаток) КАК Остаток
ИЗ
ВТ_Данные КАК Т
ГДЕ
(Т.ПродажиПрошлыйГод <> 0 ИЛИ Т.ПродажиПозапрошлыйГод <> 0)
СГРУППИРОВАТЬ ПО
Т.Номенклатура,
Т.Склад
Разберем ситуацию с фильтрацией. В условии ГДЕ мы указали, что нам нужны только те позиции, по которым были продажи. Если по товару есть остаток, но не было продаж за оба года, он не попадет в отчет. Если же задача стоит иначе — выводить все товары, по которым есть остатки, независимо от продаж — это условие нужно удалить.
При работе с виртуальными таблицами 1С важно помнить о производительности. Проанализируем несколько критических моментов:
ГДЕ.Продажи.ДокументПродажи.Склад. Каждая такая «точка» — это неявное левое соединение со всеми таблицами документов-регистраторов, что может катастрофически замедлить отчет. Если в регистре Продажи нет измерения Склад, используйте регистры, где склад является аналитикой в явном виде.Номенклатура и Склад. В этом случае СКД сама выполнит аналог FULL OUTER JOIN, корректно распределив данные по колонкам и строкам.Для того чтобы отчет выглядел структурно (сначала идет название товара, а ниже — список складов, на которых он есть или продавался), мы рекомендуем использовать механизм группировок. В итоговом запросе или в настройках СКД укажите иерархическую структуру:
Номенклатура (включая иерархию)СкладПри такой настройке суммы продаж и остатков будут автоматически агрегироваться «снизу вверх» — со складов на уровень номенклатуры. Это позволит быстро увидеть общую картину по компании и детальную информацию по каждому месту хранения.
Подводя итог, можно сказать, что ключ к созданию верного отчета по остаткам и продажам — это отказ от сложных соединений в пользу вертикального объединения данных с последующей горизонтальной группировкой ресурсов. Этот метод гарантирует точность данных и высокую скорость работы программы.