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