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