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