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