Вопрос о том, почему ЛЕВОЕ СОЕДИНЕНИЕ (или LEFT JOIN) доминирует в запросах 1С, регулярно возникает на собеседованиях и в профессиональных дискуссиях. Это не просто вопрос смекалки, а отражение глубоких логических принципов и практических особенностей платформы. Давайте вместе разберемся, почему именно этот тип соединения стал столь популярен в разработке на 1С.
Прежде чем углубляться в причины популярности ЛЕВОГО СОЕДИНЕНИЯ, давайте освежим в памяти основные типы соединений, используемые в запросах 1С (которые аналогичны стандартным SQL-соединениям).
ВНУТРЕННЕЕ СОЕДИНЕНИЕ (INNER JOIN): Мы используем ВНУТРЕННЕЕ СОЕДИНЕНИЕ, когда нам нужны только те строки, для которых нашлись совпадения в обеих соединяемых таблицах по указанному условию. Если для записи из одной таблицы нет соответствующей записи во второй, она будет исключена из результата. Это может привести к потере данных, если нам важны все записи из одной из таблиц.
ЛЕВОЕ СОЕДИНЕНИЕ (LEFT JOIN или LEFT OUTER JOIN): Когда мы применяем ЛЕВОЕ СОЕДИНЕНИЕ, мы получаем все строки из левой (первой) таблицы. Если для какой-либо строки из левой таблицы находятся соответствующие записи в правой таблице, они будут присоединены. Если же соответствий нет, поля из правой таблицы для этой строки будут заполнены значением NULL. Это ключевой момент, позволяющий сохранить полноту данных из основной таблицы.
ПРАВОЕ СОЕДИНЕНИЕ (RIGHT JOIN или RIGHT OUTER JOIN): ПРАВОЕ СОЕДИНЕНИЕ — это зеркальное отображение левого соединения. Мы получаем все строки из правой (второй) таблицы. Если для строки из правой таблицы нет соответствующей записи в левой, поля из левой таблицы будут заполнены NULL. В 1С оно используется реже, часто преобразуется в левое конструктором запросов.
ПОЛНОЕ СОЕДИНЕНИЕ (FULL JOIN или FULL OUTER JOIN): При ПОЛНОМ СОЕДИНЕНИИ мы получаем все строки из обеих таблиц. Если для записи из одной таблицы нет соответствия в другой, поля отсутствующей таблицы заполняются NULL. Используется редко из-за своей "всеобъемлющей" природы, которая часто приводит к избыточности данных или сложности в интерпретации результата.
Главная и наиболее фундаментальная причина, по которой ЛЕВОЕ СОЕДИНЕНИЕ так широко используется в запросах 1С, кроется в его логической целесообразности при необходимости сохранить все записи из одной, "основной" таблицы, дополняя их информацией из других таблиц, даже если таковая отсутствует. Рассмотрим подробнее этот аспект.
Анализ основной сущности без потерь: Представьте, что перед нами стоит задача получить полный список какой-либо сущности из нашей базы данных 1С – например, всех контрагентов, всех позиций номенклатуры, всех сотрудников. При этом мы хотим дополнить эти основные данные сведениями из связанных таблиц, такими как история заказов для контрагента, информация о производителе товара, или назначенные задачи для сотрудника. В этом случае, использование ЛЕВОГО СОЕДИНЕНИЯ гарантирует, что мы получим все записи из "основной" таблицы, даже если для некоторых из них не нашлось соответствий в связанных таблицах.
Пример: Допустим, нам необходимо вывести список всех клиентов и информацию об их последних заказах. Если бы мы использовали ВНУТРЕННЕЕ СОЕДИНЕНИЕ с таблицей заказов, клиенты, у которых еще не было ни одного заказа, просто не попали бы в результат. Это означает потерю важных данных о клиентах, которые еще не совершили покупок. ЛЕВОЕ СОЕДИНЕНИЕ же включит всех клиентов, а для тех, у кого нет заказов, поля, относящиеся к заказу, будут заполнены значением NULL. Таким образом, мы получаем полную картину, не теряя ни одного клиента из поля зрения.
ВЫБРАТЬ
Клиенты.Ссылка КАК Клиент,
Клиенты.Наименование КАК НаименованиеКлиента,
Заказы.Номер КАК НомерЗаказа,
Заказы.Дата КАК ДатаЗаказа
ИЗ
Справочник.Контрагенты КАК Клиенты
ЛЕВОЕ СОЕДИНЕНИЕ Документ.ЗаказКлиента КАК Заказы
ПО Клиенты.Ссылка = Заказы.Контрагент
В этом запросе мы увидим всех контрагентов. Если у контрагента есть заказы, соответствующие поля будут заполнены. Если заказов нет, НомерЗаказа и ДатаЗаказа будут NULL.
Гибкость в анализе и фильтрации данных: ЛЕВОЕ СОЕДИНЕНИЕ обеспечивает высокую гибкость при анализе данных. Мы можем легко отфильтровать или сгруппировать записи, основываясь на наличии или отсутствии связанных данных. Это позволяет отвечать на вопросы типа "кто из сотрудников не имеет назначенных задач?" или "какие товары ни разу не продавались?".
Пример: Нам требуется список всех сотрудников, которым на данный момент не назначены никакие задачи. С ЛЕВЫМ СОЕДИНЕНИЕМ это достигается очень просто: мы присоединяем таблицу задач к таблице сотрудников и фильтруем по условию ГДЕ Задачи.Ссылка ЕСТЬ NULL.
ВЫБРАТЬ
Сотрудники.Ссылка КАК Сотрудник,
Сотрудники.Наименование КАК ИмяСотрудника
ИЗ
Справочник.Сотрудники КАК Сотрудники
ЛЕВОЕ СОЕДИНЕНИЕ Документ.Задача КАК Задачи
ПО Сотрудники.Ссылка = Задачи.Исполнитель
ГДЕ Задачи.Ссылка ЕСТЬ NULL
Этот запрос эффективно выведет нам всех сотрудников, у которых отсутствует связь с записями в документе Задача, то есть, не назначено никаких задач. С ВНУТРЕННИМ СОЕДИНЕНИЕМ такой подход был бы невозможен, так как сотрудники без задач были бы исключены на этапе соединения.
Модель "Шампура": Очень наглядной и часто упоминаемой аналогией является модель "шампура". Представьте, что сбор данных в запросе выглядит как нанизывание ингредиентов (овощей, мяса) на шампур. "Шампур" в данном случае — это наша основная (левая) таблица. Мы "нанизываем" на неё (присоединяем с помощью ЛЕВОГО СОЕДИНЕНИЯ) дополнительные данные из других таблиц. Суть в том, что "шампур" остается целым и неизменным, а к нему добавляются только те "ингредиенты" (связанные данные), которые соответствуют условиям. Если какого-то "ингредиента" нет, "шампур" не пропадает, просто на этом месте его не будет.
Помимо логических преимуществ, распространенность ЛЕВОГО СОЕДИНЕНИЯ в 1С во многом обусловлена особенностями самой платформы и ее инструментов, в частности, поведением встроенного Конструктора Запросов.
Автоматическое преобразование ПРАВОГО СОЕДИНЕНИЯ в ЛЕВОЕ СОЕДИНЕНИЕ: Мы выясняем, что Конструктор Запросов 1С обладает характерной чертой: он имеет тенденцию преобразовывать ПРАВОЕ СОЕДИНЕНИЕ в ЛЕВОЕ СОЕДИНЕНИЕ. Это происходит даже в том случае, если вы изначально создали запрос с ПРАВЫМ СОЕДИНЕНИЕМ, а затем просто открыли его в конструкторе и сохранили, не внося никаких явных изменений в логику. Конструктор автоматически меняет таблицы местами в соединении и соответствующим образом перестраивает связи и поля в тексте запроса, тем самым унифицируя запросы на использование ЛЕВОГО СОЕДИНЕНИЯ.
Пример поведения конструктора:
// Исходный запрос, созданный вручную с ПРАВЫМ СОЕДИНЕНИЕМ
ВЫБРАТЬ
Заказы.Номер КАК НомерЗаказа,
Клиенты.Наименование КАК Клиент
ИЗ
Документ.ЗаказКлиента КАК Заказы
ПРАВОЕ СОЕДИНЕНИЕ Справочник.Контрагенты КАК Клиенты
ПО Заказы.Контрагент = Клиенты.Ссылка
Если мы затем откроем этот запрос в Конструкторе Запросов 1С и сохраним, не меняя логики получения всех клиентов (даже без заказов), он, скорее всего, будет преобразован в:
// Запрос после работы конструктора
ВЫБРАТЬ
Клиенты.Наименование КАК Клиент,
Заказы.Номер КАК НомерЗаказа
ИЗ
Справочник.Контрагенты КАК Клиенты
ЛЕВОЕ СОЕДИНЕНИЕ Документ.ЗаказКлиента КАК Заказы
ПО Клиенты.Ссылка = Заказы.Контрагент
Как мы видим, таблицы поменялись местами, а ПРАВОЕ СОЕДИНЕНИЕ трансформировалось в ЛЕВОЕ, при этом сохраняется та же логика: вывести всех клиентов и присоединенные к ним заказы, если таковые имеются.
Статистика использования в типовых конфигурациях: Мы наблюдаем, что в крупных и сложных типовых конфигурациях 1С, таких как 1С:ERP Управление предприятием, подавляющее большинство соединений – до 98% – приходится на ЛЕВОЕ СОЕДИНЕНИЕ. Эта статистика не только подтверждает общую тенденцию, но и указывает на то, что использование ЛЕВОГО СОЕДИНЕНИЯ глубоко укоренилось в стандартных практиках разработки на платформе 1С, отчасти благодаря автоматизированным инструментам.
Единообразное использование ЛЕВОГО СОЕДИНЕНИЯ по умолчанию в 1С также способствует лучшей читаемости, пониманию и поддержке кода запросов.
Логическая эквивалентность и стандартизация: Важно осознавать, что ЛЕВОЕ СОЕДИНЕНИЕ и ПРАВОЕ СОЕДИНЕНИЕ являются логически эквивалентными. Любой запрос, написанный с ПРАВЫМ СОЕДИНЕНИЕМ, мы можем переписать с ЛЕВЫМ, просто поменяв таблицы местами в секции ИЗ.
Например, для получения всех товаров и связанных с ними данных о поставщиках, мы можем написать:
ВЫБРАТЬ
Товары.Наименование КАК НаименованиеТовара,
Поставщики.Наименование КАК НаименованиеПоставщика
ИЗ
Справочник.Номенклатура КАК Товары
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Контрагенты КАК Поставщики
ПО Товары.Поставщик = Поставщики.Ссылка
Или, менее привычно для 1С, с ПРАВЫМ СОЕДИНЕНИЕМ:
ВЫБРАТЬ
Товары.Наименование КАК НаименованиеТовара,
Поставщики.Наименование КАК НаименованиеПоставщика
ИЗ
Справочник.Контрагенты КАК Поставщики
ПРАВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Товары
ПО Товары.Поставщик = Поставщики.Ссылка
Оба запроса дадут одинаковый результат: все товары (даже без указанных поставщиков) и данные поставщиков, если они есть. Поскольку результат одинаков, выбор одного из этих вариантов становится вопросом конвенции и читаемости. В экосистеме 1С эта конвенция однозначно склоняется в пользу ЛЕВОГО СОЕДИНЕНИЯ.
Единообразие: Когда большинство разработчиков придерживаются одной и той же практики (например, всегда начинают с основной таблицы и присоединяют к ней другие), это существенно упрощает чтение, понимание и поддержку чужого кода. Нам требуется меньше усилий, чтобы предсказать и интерпретировать ожидаемый результат от запроса, который начинается с ЛЕВОГО СОЕДИНЕНИЯ.
Хотя ЛЕВОЕ СОЕДИНЕНИЕ является мощным и гибким инструментом, нам следует помнить о некоторых важных аспектах его использования, чтобы избежать ошибок и оптимизировать производительность.
Фильтрация по полям правой таблицы в ГДЕ: Если мы используем ЛЕВОЕ СОЕДИНЕНИЕ, но затем добавляем условие в секции ГДЕ, которое фильтрует по полю из правой таблицы и исключает NULL значения (например, ГДЕ ПолеИзПравойТаблицы = Значение или ГДЕ ПолеИзПравойТаблицы ЕСТЬ НЕ NULL), то такое ЛЕВОЕ СОЕДИНЕНИЕ фактически превращается в ВНУТРЕННЕЕ СОЕДИНЕНИЕ. Это происходит потому, что строки, для которых в правой таблице не нашлось соответствий и поля были NULL, будут отброшены условием ГДЕ.
Пример:
ВЫБРАТЬ
Клиенты.Наименование КАК НаименованиеКлиента,
Заказы.Номер КАК НомерЗаказа
ИЗ
Справочник.Контрагенты КАК Клиенты
ЛЕВОЕ СОЕДИНЕНИЕ Документ.ЗаказКлиента КАК Заказы
ПО Клиенты.Ссылка = Заказы.Контрагент
ГДЕ Заказы.Дата >= &НачалоПериода
Этот запрос не вернет клиентов без заказов, поскольку условие Заказы.Дата >= &НачалоПериода не может быть истинным для NULL значений в Заказы.Дата. Таким образом, он эффективно отфильтрует все строки, для которых в правой таблице не нашлось соответствий, ведя себя как ВНУТРЕННЕЕ СОЕДИНЕНИЕ.
Увеличение количества строк в результате: Важно помнить, что хотя ЛЕВОЕ СОЕДИНЕНИЕ возвращает все строки из левой таблицы, количество строк в результирующей выборке может быть больше, чем в исходной левой таблице. Это произойдет, если для одной записи из левой таблицы существует несколько совпадающих записей в правой таблице.
Пример: Если один клиент сделал несколько заказов, то при соединении таблицы клиентов с таблицей заказов через ЛЕВОЕ СОЕДИНЕНИЕ, каждый заказ клиента будет представлен отдельной строкой в результате, при этом данные клиента будут повторяться.
Оптимизация производительности: В сложных запросах, работающих с большими объемами данных, особенно при использовании множественных соединений, мы рекомендуем внимательно относиться к производительности. В таких случаях, вместо очень длинных и сложных цепочек соединений или вложенных запросов, иногда более эффективным подходом может быть использование временных таблиц для предварительного формирования промежуточных результатов. Это связано с тем, что оптимизатору СУБД порой сложнее оценить количество записей во вложенных запросах и построить оптимальный план выполнения запроса, что может привести к замедлению работы.
Подводя итоги, мы можем уверенно утверждать, что доминирование ЛЕВОГО СОЕДИНЕНИЯ в запросах 1С обусловлено мощным сочетанием нескольких факторов. Это и его логическая целесообразность, позволяющая сохранять полноту данных из основной таблицы при добавлении связанной информации, и особенности инструментов платформы 1С, в частности, автоматическое преобразование в Конструкторе Запросов. Также важную роль играют стремление к единообразию и повышению читаемости кода, что упрощает совместную разработку и поддержку. Понимание этих причин не только помогает нам отвечать на "вопросы на смекалку", но и дает возможность писать более эффективные, надежные и понятные запросы в системе 1С.