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