Как правильно сгруппировать данные в запросе с ОБЪЕДИНИТЬ ВСЕ в 1С

Программист 1С v8.3 (Управляемые формы) Управленческий учет IT и автоматизация бизнеса
← На главную

При разработке отчетов и выборок в 1С часто возникает задача собрать данные из разных таблиц или по разным условиям в единый результат с последующей группировкой. Нередко при использовании конструкции ОБЪЕДИНИТЬ ВСЕ запрос начинает работать медленно, а итоговые строки не сворачиваются в одну. Проанализируем типичные ошибки при формировании таких запросов и разберем правильные способы решения задачи.

Анализ основных ошибок в исходном запросе

Рассмотрим структуру исходного запроса, в котором пытаются получить даты установки двух разных статусов для документа ПеремещениеТоваров:

  1. Несовпадение типов данных в объединяемых полях: В первой ветке запроса поле Отгружено заполняется пустой строкой "", а во второй — полем даты из регистра. Аналогично для поля Принято. Смешивание строк и дат приводит к формированию колонки составного типа (Строка + Дата). По полям составного типа СУБД не может корректно применять агрегатные функции (например, МАКСИМУМ), а неявное приведение типов вызывает существенные задержки при выполнении запроса.
  2. Искажение логики левого соединения: Во второй ветке запроса используется ЛЕВОЕ СОЕДИНЕНИЕ, однако в секции ГДЕ накладывается условие ПеремещениеТоваров.Организация = &Организация на правую таблицу. Любой фильтр по правой таблице в секции ГДЕ фактически превращает левое соединение во внутреннее, отсекая строки, для которых соответствие не найдено.
  3. Попытка группировки без агрегатных функций: Чтобы объединить две строки (где в одной заполнена дата принятия, а во второй — дата отгрузки) в одну общую строку документа, необходимо обернуть объединенный результат во внешний запрос и применить функции МАКСИМУМ или МИНИМУМ к полям дат.

Способ 1. Использование литерала пустой даты и внешнего запроса с группировкой

Разберем по шагам, как исправить исходный запрос с объединением. Главное правило: если в одной ветке поле имеет тип даты, во всех остальных ветках на этом месте должен стоять литерал пустой даты ДАТАВРЕМЯ(1, 1, 1).

Посмотрим на пример правильного запроса с вложенным объединением и последующей агрегацией:


ВЫБРАТЬ
    ВложенныйЗапрос.Документ КАК Док,
    ВложенныйЗапрос.СкладПолучатель КАК СкладПолучатель,
    ВложенныйЗапрос.ДатаДокумента КАК ДатаДокумента,
    МАКСИМУМ(ВложенныйЗапрос.Принято) КАК Принято,
    МАКСИМУМ(ВложенныйЗапрос.Отгружено) КАК Отгружено
ИЗ
    (ВЫБРАТЬ
        ПеремещениеТоваров.Ссылка КАК Документ,
        ПеремещениеТоваров.СкладПолучатель КАК СкладПолучатель,
        ПеремещениеТоваров.Дата КАК ДатаДокумента,
        ИсторияСтатусов.Дата КАК Принято,
        ДАТАВРЕМЯ(1, 1, 1) КАК Отгружено
    ИЗ
        Документ.ПеремещениеТоваров КАК ПеремещениеТоваров
            ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ИсторияСтатусовСправочниковИДокументов КАК ИсторияСтатусов
            ПО ПеремещениеТоваров.Ссылка = ИсторияСтатусов.Объект
                И ИсторияСтатусов.Статус = ЗНАЧЕНИЕ(Справочник.СтатусыСправочниковИДокументов.ПТПринято)
    ГДЕ
        ПеремещениеТоваров.Проведен
        И ПеремещениеТоваров.Организация = &Организация

    ОБЪЕДИНИТЬ ВСЕ

    ВЫБРАТЬ
        ПеремещениеТоваров.Ссылка,
        ПеремещениеТоваров.СкладПолучатель,
        ПеремещениеТоваров.Дата,
        ДАТАВРЕМЯ(1, 1, 1),
        ИсторияСтатусов.Дата
    ИЗ
        Документ.ПеремещениеТоваров КАК ПеремещениеТоваров
            ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ИсторияСтатусовСправочниковИДокументов КАК ИсторияСтатусов
            ПО ПеремещениеТоваров.Ссылка = ИсторияСтатусов.Объект
                И ИсторияСтатусов.Статус = ЗНАЧЕНИЕ(Справочник.СтатусыСправочниковИДокументов.ПТОтгружено)
    ГДЕ
        ПеремещениеТоваров.Проведен
        И ПеремещениеТоваров.Организация = &Организация) КАК ВложенныйЗапрос

СГРУППИРОВАТЬ ПО
    ВложенныйЗапрос.Документ,
    ВложенныйЗапрос.СкладПолучатель,
    ВложенныйЗапрос.ДатаДокумента

Обратим внимание: функция МАКСИМУМ выбирает непустую дату вместо 01.01.0001, что позволяет получить одну строку на каждый документ со всеми заполненными статусами.

Способ 2. Двойное левое соединение (рекомендуемый вариант)

Проанализируем архитектурную альтернативу: если требуется получить разные статусы одного и того же объекта, использование конструкции ОБЪЕДИНИТЬ ВСЕ является избыточным. Гораздо эффективнее построить запрос с двойным левым соединением регистра к базовой таблице документов.

Разберем реализацию этого подхода:


ВЫБРАТЬ
    ПеремещениеТоваров.Ссылка КАК Док,
    ПеремещениеТоваров.СкладПолучатель КАК СкладПолучатель,
    ПеремещениеТоваров.Дата КАК ДатаДокумента,
    ЕСТЬNULL(СтатусПринято.Дата, ДАТАВРЕМЯ(1, 1, 1)) КАК Принято,
    ЕСТЬNULL(СтатусОтгружено.Дата, ДАТАВРЕМЯ(1, 1, 1)) КАК Отгружено
ИЗ
    Документ.ПеремещениеТоваров КАК ПеремещениеТоваров
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ИсторияСтатусовСправочниковИДокументов КАК СтатусПринято
        ПО ПеремещениеТоваров.Ссылка = СтатусПринято.Объект
            И (СтатусПринято.Статус = ЗНАЧЕНИЕ(Справочник.СтатусыСправочниковИДокументов.ПТПринято))
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ИсторияСтатусовСправочниковИДокументов КАК СтатусОтгружено
        ПО ПеремещениеТоваров.Ссылка = СтатусОтгружено.Объект
            И (СтатусОтгружено.Статус = ЗНАЧЕНИЕ(Справочник.СтатусыСправочниковИДокументов.ПТОтгружено))
ГДЕ
    ПеремещениеТоваров.Проведен
    И ПеремещениеТоваров.Организация = &Организация

Преимущества второго способа:

  1. Производительность: СУБД выполняет одно сканирование базовой таблицы документов вместо двух отдельных запросов и последующего объединения.
  2. Простота чтения: Запрос не требует вложенных подзапросов и громоздких секций СГРУППИРОВАТЬ ПО.
  3. Корректность типов: Функция ЕСТЬNULL гарантирует возврат типизированного значения даты даже при отсутствии записи в регистре.

Важные правила оптимизации запросов

При работе с объединениями и группировками в 1С всегда следует придерживаться следующих правил:

  1. Всегда используйте ОБЪЕДИНИТЬ ВСЕ вместо ОБЪЕДИНИТЬ, если нет необходимости исключать полные дубли строк. Простое ОБЪЕДИНИТЬ заставляет СУБД выполнять неявный DISTINCT, что сильно замедляет работу.
  2. Соблюдайте строгую типизацию во всех ветках объединения: для дат используйте ДАТАВРЕМЯ(1, 1, 1), для чисел — 0, для ссылочных полей — ЗНАЧЕНИЕ(Справочник.Имя.ПустаяСсылка).
  3. Условия на присоединенную правую таблицу при левом соединении всегда помещайте в секцию ПО, а не в секцию ГДЕ.
← На главную