При разработке отчетов в системе 1С:Предприятие часто возникает необходимость собрать данные из нескольких источников в одну таблицу. Например, вам нужно увидеть одновременно остатки товаров на складах, зарезервированное количество и объем продаж. Каждый из этих показателей хранится в своем регистре: остатки — в ТоварыНаСкладах, резервы — в ТоварыВРезервеНаСкладах, а продажи — в Продажи. В этой статье мы подробно разберем, как правильно использовать конструкцию ОБЪЕДИНИТЬ, как решать проблемы с несовпадением имен полей и как корректно группировать результаты.
Когда мы пытаемся объединить запросы из разных таблиц, мы сталкиваемся с тем, что структура этих таблиц различается. В одной таблице есть поле КоличествоОстаток, в другой — КоличествоОборот, а в третьей поле склада может называться СкладОрдер. Если просто написать несколько запросов через ОБЪЕДИНИТЬ, платформа выведет их друг под другом, и данные не «схлопнутся» в одну строку по конкретной номенклатуре и складу. Кроме того, важно учитывать производительность и правильность расчета итогов, особенно если нужно вычесть возвраты из общих продаж.
Рассмотрим ситуацию: нам нужно получить данные по остаткам, резервам и продажам. Чтобы объединение сработало корректно, количество колонок и их порядок во всех объединяемых запросах должны быть одинаковыми. Мы применим метод «заглушек» — будем подставлять 0 в те колонки, которых нет в текущем источнике данных.
Проанализируем пример кода, где мы формируем три части одного большого запроса:
ВЫБРАТЬ
ТоварыНаСкладах.Склад КАК Склад,
ТоварыНаСкладах.Номенклатура КАК Номенклатура,
СУММА(ТоварыНаСкладах.КоличествоОстаток) КАК Остаток,
0 КАК Резерв,
0 КАК Продажи
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки(&ДатаАктуальности, ) КАК ТоварыНаСкладах
СГРУППИРОВАТЬ ПО
ТоварыНаСкладах.Склад,
ТоварыНаСкладах.Номенклатура
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
ТоварыВРезерве.Склад,
ТоварыВРезерве.Номенклатура,
0,
СУММА(ТоварыВРезерве.КоличествоОстаток),
0
ИЗ
РегистрНакопления.ТоварыВРезервеНаСкладах.Остатки(&ДатаАктуальности, ) КАК ТоварыВРезерве
СГРУППИРОВАТЬ ПО
ТоварыВРезерве.Склад,
ТоварыВРезерве.Номенклатура
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
ПродажиОбороты.ДокументПродажи.Склад,
ПродажиОбороты.Номенклатура,
0,
0,
СУММА(ПродажиОбороты.КоличествоОборот)
ИЗ
РегистрНакопления.Продажи.Обороты(&ДатаНачала, &ДатаАктуальности, , ) КАК ПродажиОбороты
СГРУППИРОВАТЬ ПО
ПродажиОбороты.ДокументПродажи.Склад,
ПродажиОбороты.Номенклатура
Важный момент: Итоговые имена колонок всей выборки определяются по первому запросу. Поэтому именно в первом блоке ВЫБРАТЬ крайне важно задать понятные псевдонимы через ключевое слово КАК. Если в третьем запросе поле называется СкладОрдер, оно автоматически станет Склад в итоговом результате, так как оно стоит на первой позиции.
Выясним причину, по которой профессиональные разработчики 1С практически всегда используют ОБЪЕДИНИТЬ ВСЕ.
DISTINCT). Это требует значительных ресурсов сервера СУБД, так как системе нужно сравнить каждую строку с каждой.Поскольку мы всё равно будем делать финальную группировку данных позже, нам не нужно, чтобы система тратила время на поиск дублей на этапе объединения. Поэтому всегда выбирайте ОБЪЕДИНИТЬ ВСЕ.
Если мы выполним запрос из первого шага, мы получим несколько строк для одной и той же номенклатуры: в одной строке будет заполнен остаток (а резерв и продажи будут 0), в другой — резерв, в третьей — продажи. Чтобы объединить их в одну красивую строку, нужно поместить результат во временную таблицу и выполнить еще один запрос.
Рассмотрим, как это сделать по шагам:
// Помещаем все данные в одну временную таблицу
ВЫБРАТЬ
ИсходныеДанные.Склад КАК Склад,
ИсходныеДанные.Номенклатура КАК Номенклатура,
ИсходныеДанные.Остаток КАК Остаток,
ИсходныеДанные.Резерв КАК Резерв,
ИсходныеДанные.Продажи КАК Продажи
ПОМЕСТИТЬ ВТ_ОбщиеДанные
ИЗ
(
// Здесь вставляем наш запрос с ОБЪЕДИНИТЬ ВСЕ из Шага 1
) КАК ИсходныеДанные
;
////////////////////////////////////////////////////////////////////////////////
// Группируем данные из временной таблицы
ВЫБРАТЬ
ВТ_ОбщиеДанные.Склад КАК Склад,
ВТ_ОбщиеДанные.Номенклатура КАК Номенклатура,
СУММА(ВТ_ОбщиеДанные.Остаток) КАК Остаток,
СУММА(ВТ_ОбщиеДанные.Резерв) КАК Резерв,
СУММА(ВТ_ОбщиеДанные.Продажи) КАК Продажи
ИЗ
ВТ_ОбщиеДанные КАК ВТ_ОбщиеДанные
СГРУППИРОВАТЬ ПО
ВТ_ОбщиеДанные.Склад,
ВТ_ОбщиеДанные.Номенклатура
Этот подход гарантирует, что на выходе вы получите одну строку на каждую уникальную пару «Склад + Номенклатура» со всеми просуммированными показателями.
Разберем ситуацию, когда из объема продаж нужно вычесть возвраты от покупателей. Если возвраты фиксируются в том же регистре Продажи, но с другим видом движения или в другом регистре, самый простой способ — это умножить количество на -1 в соответствующей части запроса.
Например, в блоке объединения для возвратов:
ВЫБРАТЬ
Возвраты.Склад,
Возвраты.Номенклатура,
0, // Остаток
0, // Резерв
-СУММА(Возвраты.КоличествоОборот) // Вычитаем продажи
ИЗ
РегистрНакопления.Продажи.Обороты(...) КАК Возвраты
...
При финальной группировке функция СУММА просто сложит положительные продажи и отрицательные возвраты, и вы получите корректный чистый оборот.
Для того чтобы ваш запрос работал максимально быстро даже на больших объемах данных, придерживайтесь следующих правил:
ВТ_ОбщиеДанные обязательно добавьте индекс по полям, по которым идет группировка. Используйте инструкцию ИНДЕКСИРОВАТЬ ПО Склад, Номенклатура.Остатки и Обороты, а не в условие ГДЕ. Это позволит 1С отсечь лишние данные в самом начале выполнения.0 или NULL используйте конструкцию ЗНАЧЕНИЕ(Документ.НазваниеДокумента.ПустаяСсылка). Это поможет избежать ошибок при дальнейшей обработке результата.Таким образом, использование конструкции ОБЪЕДИНИТЬ ВСЕ в связке с временными таблицами является стандартом разработки в 1С 8.3. Это позволяет создавать гибкие, читаемые и высокопроизводительные запросы для самых сложных аналитических отчетов.