Работа с базами данных в 1С:Предприятие часто требует объединения данных из нескольких таблиц. Без этого невозможно создать сложные отчеты, анализировать взаимосвязанную информацию или автоматизировать бизнес-процессы. Однако начинающие разработчики нередко сталкиваются с трудностями: как правильно соединить таблицы, чтобы не потерять данные? Какие типы соединений использовать в разных сценариях? И почему иногда результат запроса пуст, хотя данные в таблицах есть?
Эта статья поможет разобраться в синтаксисе объединения таблиц в языке запросов 1С, избежать типичных ошибок и научиться писать эффективные запросы. Мы рассмотрим все основные виды соединений — от простого ВЫБРАТЬ до сложных конструкций с ПОЛНОЕ СОЕДИНЕНИЕ, а также разберем практические примеры для реальных задач учета.
Особое внимание уделим скрытым нюансам производительности, которые могут замедлить выполнение запросов в больших базах. Эти знания пригодятся как администраторам для оптимизации работы системы, так и программистам для создания быстрых отчетов.
Основные типы соединений таблиц в 1С
В языке запросов 1С 8.3 существует пять основных типов соединений, каждый из которых решает свои задачи. Выбор неправильного типа — самая распространенная ошибка, которая приводит к потере данных или дублированию строк.
Рассмотрим их на примере двух таблиц: Справочник.Контрагенты (с полями Ссылка, Наименование) и Документ.ЗаказыПокупателей (с полями Ссылка, Контрагент, Сумма).
- 🔹 ВНУТРЕННЕЕ СОЕДИНЕНИЕ (
СОЕДИНИТЬ) — возвращает только строки, где есть совпадения в обеих таблицах. Самый строгий тип. - 🔸 ЛЕВОЕ СОЕДИНЕНИЕ (
ЛЕВОЕ СОЕДИНЕНИЕ) — включает все строки из левой таблицы, даже если в правой нет совпадений (для них поля правой таблицы будутNULL). - 🔶 ПРАВОЕ СОЕДИНЕНИЕ (
ПРАВОЕ СОЕДИНЕНИЕ) — аналогично левому, но сохраняет все строки из правой таблицы. - 🟣 ПОЛНОЕ СОЕДИНЕНИЕ (
ПОЛНОЕ СОЕДИНЕНИЕ) — возвращает все строки из обеих таблиц, заполняяNULLтам, где нет совпадений. - 🟠 ПЕРЕКРЕСТНОЕ СОЕДИНЕНИЕ (
ПЕРЕКРЕСТНОЕ СОЕДИНЕНИЕ) — создает декартово произведение (каждая строка первой таблицы соединяется с каждой строкой второй).
На практике чаще всего используются внутреннее и левое соединения. Например, чтобы показать всех контрагентов и их заказы (включая тех, кто еще не делал заказов), нужно использовать левое соединение.
Синтаксис запроса для объединения таблиц
Базовый синтаксис объединения таблиц в 1С выглядит так:
ВЫБРАТЬ
Таблица1.Поле1,
Таблица1.Поле2,
Таблица2.Поле1 КАК ПолеИзТаблицы2
ИЗ
Таблица1 КАК Таблица1
[ТИП_СОЕДИНЕНИЯ] СОЕДИНЕНИЕ Таблица2 КАК Таблица2
ПО Таблица1.КлючевоеПоле = Таблица2.КлючевоеПоле
Где [ТИП_СОЕДИНЕНИЯ] может быть опущен (по умолчанию это ВНУТРЕННЕЕ) или явно указан как один из пяти типов.
Пример запроса с внутренним соединением для выборки заказов с информацией о контрагентах:
ВЫБРАТЬ
Заказы.Ссылка КАК НомерЗаказа,
Заказы.Дата,
Контрагенты.Наименование КАК Контрагент,
Заказы.Сумма
ИЗ
Документ.ЗаказыПокупателей КАК Заказы
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Контрагенты КАК Контрагенты
ПО Заказы.Контрагент = Контрагенты.Ссылка
ГДЕ
Заказы.Дата МЕЖДУ &НачалоПериода И &КонецПериода
Всегда указывайте псевдонимы таблиц (КАК Таблица1) — это улучшает читаемость запроса и позволяет избежать ошибок при повторяющихся именах полей.
Практические примеры объединения таблиц
Разберем реальные сценарии, с которыми сталкиваются разработчики 1С.
Пример 1. Отчет по продажам с данными о менеджерах
Нужно показать все продажи с указанием менеджера, который их провел, даже если у некоторых продаж менеджер не указан.
ВЫБРАТЬ
Продажи.Номер КАК НомерДокумента,
Продажи.Дата,
ЕСЛИ Менеджеры.Наименование ЕСТЬ NULL
ТОГДА "Без менеджера"
ИНАЧЕ Менеджеры.Наименование
КАК Менеджер,
Продажи.Сумма
ИЗ
Документ.РеализацияТоваровУслуг КАК Продажи
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Пользователи КАК Менеджеры
ПО Продажи.Ответственный = Менеджеры.Ссылка
Пример 2. Анализ остатков товаров с ценами
Требуется показать остатки товаров на складе вместе с их последними закупочными ценами.
ВЫБРАТЬ
Остатки.Номенклатура КАК Товар,
Остатки.КоличествоОстаток КАК Остаток,
Цены.Цена КАК ПоследняяЗакупочнаяЦена
ИЗ
РегистрНакопления.ОстаткиТоваров.Остатки КАК Остатки
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&ТекущаяДата,) КАК Цены
ПО Остатки.Номенклатура = Цены.Номенклатура
Пример 3. Сравнение плановых и фактических продаж
Необходимо сопоставить плановые показатели продаж (из регистра сведений) с фактическими (из документов реализации).
ВЫБРАТЬ
Планы.Номенклатура КАК Товар,
Планы.ПлановыйОбъем КАК План,
ЕСЛИ Факты.Количество ЕСТЬ NULL ТОГДА 0 ИНАЧЕ Факты.Количество КАК Факт
ИЗ
РегистрСведений.ПланыПродаж.СрезПоследних(&КонецМесяца,) КАК Планы
ЛЕВОЕ СОЕДИНЕНИЕ (
ВЫБРАТЬ
Реализация.Номенклатура,
СУММА(Реализация.Количество) КАК Количество
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК Реализация
ГДЕ
Реализация.Ссылка.Дата МЕЖДУ &НачалоМесяца И &КонецМесяца
СГРУППИРОВАТЬ ПО
Реализация.Номенклатура
) КАК Факты
ПО Планы.Номенклатура = Факты.Номенклатура
Что делать если соединение работает слишком медленно?
Если запрос с соединением выполняется долго, попробуйте:
1. Добавить индексы на поля, по которым происходит соединение
2. Разбить сложный запрос на несколько простых с временными таблицами
3. Использовать предварительную выборку данных через ВЫБРАТЬ РАЗЛИЧНЫЕ
4. Проверить, не тянутся ли лишние поля (выбирайте только необходимые)
Типичные ошибки при объединении таблиц
Даже опытные разработчики иногда допускают ошибки, которые приводят к некорректным результатам или медленной работе запросов.
⚠️ Внимание: Если в условии соединения используете неравенство (<>,>,<), результат может содержать декартово произведение строк. Это сильно замедляет выполнение!
- 🚫 Ошибка 1. Забыли указать условие соединения (
ПО) — в результате получите перекрестное соединение со всеми возможными комбинациями строк. - 🚫 Ошибка 2. Используете
ВНУТРЕННЕЕ СОЕДИНЕНИЕ, когда нужноЛЕВОЕ— теряете строки из основной таблицы. - 🚫 Ошибка 3. Соединяете по полям разных типов (например,
СсылкаиСтрока) — это вызовет ошибку выполнения. - 🚫 Ошибка 4. Не учитываете
NULL-значения при левом соединении — забываете обработать их черезЕСЛИ...ТОГДА. - 🚫 Ошибка 5. Делаете соединение по вычисляемым полям (например,
ПО ЛЕВО(Таблица.Поле, 3) = ...) — это блокирует использование индексов.
Частая проблема — когда запрос возвращает больше строк, чем ожидалось. Это обычно происходит из-за:
- Дублирующихся значений в поле соединения
- Неправильного типа соединения (например, использовали
ПОЛНОЕвместоВНУТРЕННЕГО) - Отсутствия условия в секции
ГДЕ, которое должно фильтровать данные
Указан правильный тип соединения|Есть условие ПО с корректными полями|Поля соединения имеют совместимые типы|Обработаны возможные NULL-значения|Выбираются только необходимые поля-->
Оптимизация запросов с соединениями
Соединения таблиц могут значительно замедлять выполнение запросов, особенно в больших базах данных. Вот ключевые приемы оптимизации:
1. Используйте индексы
Убедитесь, что поля, по которым происходит соединение, проиндексированы. В 1С индексы автоматически создаются для:
- Полей типа
Ссылка - Реквизитов с свойством "Индексировать"
- Полей, участвующих в подчиненности (например,
Родительв иерархических справочниках)
2. Ограничивайте объем данных
Добавьте условия в секцию ГДЕ, чтобы отфильтровать данные до соединения:
ВЫБРАТЬ ...
ИЗ Документ.ЗаказыПокупателей КАК Заказы
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Контрагенты КАК Контрагенты
ПО Заказы.Контрагент = Контрагенты.Ссылка
ГДЕ
Заказы.Дата >= &НачалоПериода -- Фильтрация ДО соединения
И Контрагенты.ПометкаУдаления = ЛОЖЬ
3. Избегайте соединений с подзапросами
Подзапросы в секции СОЕДИНЕНИЕ выполняются для каждой строки, что сильно тормозит запрос. Лучше использовать временные таблицы:
ВЫБРАТЬ ...
ИЗ Документ.ЗаказыПокупателей КАК Заказы
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТДополнительнаяИнформация КАК ДопИнфо
ПО Заказы.Ссылка = ДопИнфо.СсылкаНаДокумент
// Предварительно заполненная временная таблица
ВТ ДополнительнаяИнформация
4. Используйте СГРУППИРОВАТЬ ПО до соединения
Если вам нужны агрегированные данные, сначала сгруппируйте их, а потом соединяйте:
ВЫБРАТЬ ...
ИЗ
(ВЫБРАТЬ
Контрагент,
СУММА(Сумма) КАК Итого
ИЗ Документ.ЗаказыПокупателей
ГДЕ Дата МЕЖДУ &Начало И &Конец
СГРУППИРОВАТЬ ПО Контрагент) КАК Итоги
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Контрагенты КАК Контрагенты
ПО Итоги.Контрагент = Контрагенты.Ссылка
⚠️ Внимание: В последних версиях 1С:Предприятие 8.3 изменился алгоритм оптимизации запросов. Некоторые приемы, которые раньше ускоряли выполнение (например, явное указание ИНДЕКСИРОВАТЬ ПО), теперь могут давать обратный эффект. Всегда тестируйте производительность на реальных данных.
Сложные сценарии: несколько соединений в одном запросе
В реальных задачах часто требуется соединять более двух таблиц. Здесь важно правильно расставить приоритеты соединений и учитывать их типы.
Пример 1. Цепочка соединений
Нужно показать заказы с информацией о контрагенте, менеджере и статусе оплаты:
ВЫБРАТЬ
Заказы.Номер,
Контрагенты.Наименование КАК Клиент,
Менеджеры.Наименование КАК Ответственный,
СтатусыОплаты.Наименование КАК СтатусОплаты
ИЗ
Документ.ЗаказыПокупателей КАК Заказы
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Контрагенты КАК Контрагенты
ПО Заказы.Контрагент = Контрагенты.Ссылка
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Пользователи КАК Менеджеры
ПО Заказы.Ответственный = Менеджеры.Ссылка
ЛЕВОЕ СОЕДИНЕНИЕ Перечисление.СтатусыОплаты КАК СтатусыОплаты
ПО Заказы.СтатусОплаты = СтатусыОплаты.Ссылка
Пример 2. Разные типы соединений в одном запросе
Требуется показать все товары (даже те, что никогда не продавались), но только реализации за последний месяц:
ВЫБРАТЬ
Товары.Наименование КАК Товар,
Продажи.Количество КАК ПроданоЗаМесяц
ИЗ
Справочник.Номенклатура КАК Товары
ЛЕВОЕ СОЕДИНЕНИЕ (
ВЫБРАТЬ
ТоварыВРеализации.Номенклатура,
СУММА(ТоварыВРеализации.Количество) КАК Количество
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК ТоварыВРеализации
ГДЕ
ТоварыВРеализации.Ссылка.Дата >= НачалоМесяца(ТЕКУЩАЯДАТА())
СГРУППИРОВАТЬ ПО
ТоварыВРеализации.Номенклатура
) КАК Продажи
ПО Товары.Ссылка = Продажи.Номенклатура
Пример 3. Соединение с подчиненными таблицами
Нужно получить данные из документа и его табличной части:
ВЫБРАТЬ
Заказы.Номер КАК НомерЗаказа,
Товары.Номенклатура КАК Товар,
Товары.Количество,
Товары.Цена,
Контрагенты.Наименование КАК Покупатель
ИЗ
Документ.ЗаказыПокупателей КАК Заказы
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.ЗаказыПокупателей.Товары КАК Товары
ПО Заказы.Ссылка = Товары.Ссылка
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Контрагенты КАК Контрагенты
ПО Заказы.Контрагент = Контрагенты.Ссылка
При нескольких соединениях порядок важен! Начинайте с самой "узкой" таблицы (с наименьшим количеством строк), затем присоединяйте более широкие. Это сокращает объем промежуточных данных.
Альтернативные способы объединения данных
Иногда вместо соединений таблиц удобнее использовать другие подходы:
1. Подзапросы в секции ПОЛЕ
Когда нужно получить одно значение из другой таблицы:
ВЫБРАТЬ
Заказы.Номер,
Заказы.Дата,
(ВЫБРАТЬ ПЕРВЫЕ 1
Контрагенты.Наименование
ИЗ
Справочник.Контрагенты КАК Контрагенты
ГДЕ
Контрагенты.Ссылка = Заказы.Контрагент) КАК Контрагент
ИЗ
Документ.ЗаказыПокупателей КАК Заказы
2. Временные таблицы
Полезны для сложных многоэтапных выборок:
// Сначала сохраняем промежуточные данные
ВЫБРАТЬ
Номенклатура,
СУММА(Количество) КАК Продано
ПОМЕСТИТЬ ВТПродано
ИЗ
Документ.РеализацияТоваровУслуг.Товары
ГДЕ
Ссылка.Дата >= &НачалоПериода
СГРУППИРОВАТЬ ПО
Номенклатура
ИНДЕКСИРОВАТЬ ПО
Номенклатура;
// Затем используем их в основном запросе
ВЫБРАТЬ
Товары.Наименование,
ЕСЛИ Продано.Продано ЕСТЬ NULL ТОГДА 0 ИНАЧЕ Продано.Продано КАК Количество
ИЗ
Справочник.Номенклатура КАК Товары
ЛЕВОЕ СОЕДИНЕНИЕ ВТПродано КАК Продано
ПО Товары.Ссылка = Продано.Номенклатура
3. Объединение результатов (ОБЪЕДИНИТЬ)
Когда нужно совместить данные по вертикали (добавить строки), а не по горизонтали:
ВЫБРАТЬ
Номенклатура КАК Товар,
КоличествоОстаток КАК Количество,
"Остаток" КАК ТипДанных
ИЗ
РегистрНакопления.ОстаткиТоваров.Остатки(&ТекущаяДата, )
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
Номенклатура КАК Товар,
Количество КАК Количество,
"В пути" КАК ТипДанных
ИЗ
РегистрНакопления.ТоварыВПути.Остатки(&ТекущаяДата, )
| Метод | Когда использовать | Плюсы | Минусы |
|---|---|---|---|
| СОЕДИНЕНИЕ | Нужно объединить данные по ключу | Читаемый синтаксис, хорошая производительность | Может быть медленным при большом объеме данных |
| Подзапросы | Нужно одно значение из другой таблицы | Простота для простых случаев | Плохая производительность при множественных вызовах |
| Временные таблицы | Сложные многоэтапные выборки | Хорошая производительность, гибкость | Более сложный код, требует очистки |
| ОБЪЕДИНИТЬ | Нужно совместить похожие наборы данных | Простота для вертикального объединения | Не подходит для горизонтального объединения |
Отладка и тестирование запросов с соединениями
Даже опытные разработчики сталкиваются с ситуациями, когда запрос возвращает неожиданные результаты. Вот как эффективнее отлаживать такие случаи:
1. Проверяйте промежуточные результаты
Разбивайте сложный запрос на части и выполняйте их по отдельности. Например, сначала проверьте подзапрос в секции СОЕДИНЕНИЕ, затем основную выборку.
2. Используйте конструктор запросов
В 1С:Предприятие есть встроенный конструктор запросов (Новый Запрос → Конструктор запроса), который визуально показывает связи между таблицами и помогает избежать синтаксических ошибок.
3. Анализируйте план выполнения
Чтобы понять, почему запрос работает медленно:
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ ... ваш запрос ...";
Объяснение = Запрос.Выполнить().ОбъяснитьЗапрос();
Сообщить(Объяснение.Текст);
4. Проверяйте данные на NULL
Если левое соединение возвращает меньше строк, чем ожидалось, вероятно, в основной таблице есть NULL-значения в поле соединения. Добавьте проверку:
ВЫБРАТЬ
COUNT(*)
ИЗ
Документ.ЗаказыПокупателей
ГДЕ
Контрагент ЕСТЬ NULL
5. Тестируйте на маленьких данных
Если запрос выполняется долго, сначала проверьте его на небольшой выборке:
ВЫБРАТЬ ПЕРВЫЕ 100 ...
// или
ГДЕ Дата >= ДобавитьМесяц(ТЕКУЩАЯДАТА(), -1)
⚠️ Внимание: В конфигурациях с большим количеством данных (более 1 млн записей в таблицах) некоторые оптимизации запросов могут работать не так, как ожидается. Всегда тестируйте производительность на реальном объеме данных, а не только на тестовой базе.
FAQ: Частые вопросы по объединению таблиц в 1С
Можно ли соединять таблицы из разных баз данных в одном запросе?
Нет, в языке запросов 1С нельзя напрямую соединять таблицы из разных баз. Для этого нужно:
- Использовать
HTTPСервисилиCOM-Соединениедля получения данных из внешней базы - Сохранить данные во временную таблицу в текущей базе
- Выполнить соединение с временной таблицей
Альтернатива — использовать распределенные информационные базы (РИБ) или обмен данными через XML/JSON.
Почему при левом соединении не показываются строки без совпадений?
Это происходит если:
- В условии
ГДЕесть фильтр по полю из правой таблицы (перенесите его в условиеСОЕДИНЕНИЕ) - Поле соединения в основной таблице содержит
NULL - Используется не
ЛЕВОЕ СОЕДИНЕНИЕ, а другое
Проверьте запрос с конструкцией:
ВЫБРАТЬ COUNT(*)
ИЗ ЛеваяТаблица КАК Л
ЛЕВОЕ СОЕДИНЕНИЕ ПраваяТаблица КАК П
ПО Л.Ключ = П.Ключ
ГДЕ П.Ключ ЕСТЬ NULL -- Должно вернуть строки без совпадений
Как соединить таблицу саму с собой (self join)?
Это нужно для сравнения строк одной таблицы, например, чтобы найти дубли или иерархические связи. Пример — поиск документов, которые ссылаются на другие документы того же типа:
ВЫБРАТЬ
Основной.Номер КАК ОсновнойДокумент,
Ссылающийся.Номер КАК СсылаетсяНаНего
ИЗ
Документ.ЗаказыПокупателей КАК Основной
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.ЗаказыПокупателей КАК Ссылающийся
ПО Основной.Ссылка = Ссылающийся.РодительскийЗаказ
Обязательно используйте разные псевдонимы для одной таблицы (КАК Основной и КАК Ссылающийся).
Можно ли соединять таблицы по нескольким полям одновременно?
Да, в условии ПО можно указывать несколько полей через И:
ВЫБРАТЬ ...
ИЗ Таблица1 КАК Т1
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Таблица2 КАК Т2
ПО Т1.Поле