Спецификации соединений

Язык запросов

Синтаксис и применение Спецификации соединений в платформе 1С:Предприятие. Описание и примеры кода.

Полное описание, параметры и ограничения

Спецификации соединений

При определении нескольких источников в списке источников для каждой записи из первой таблицы-источника осуществляется выборка из второй таблицы-источника, и т. д. Таким образом, в результате запроса формируются все возможные комбинации всех записей из всех указанных источников.

Пример:

ВЫБРАТЬ
Контрагенты.Ссылка КАК Контрагент,
Банки.Ссылка КАК Банк

ИЗ
Справочник.Контрагенты КАК Контрагенты,
Справочник.Банки КАк Банки

Результат запроса:

Контрагент

Банк

Поставщики

АКБ ИвестБанк

Поставщики

АКБ ПромСтройБанк

Трикотажная фабрика "Заря"

АКБ ИвестБанк

Трикотажная фабрика "Заря"

АКБ ПромСтройБанк

Фабрика джинсовой одежды

АКБ ИвестБанк

Фабрика джинсовой одежды

АКБ ПромСтройБанк

Покупатели

АКБ ИвестБанк

Покупатели

АКБ ПромСтройБанк

Ярмарка одежды

АКБ ИвестБанк

Ярмарка одежды

АКБ ПромСтройБанк

Торговый дом "Буденовский"

АКБ ИвестБанк

Торговый дом "Буденовский"

АКБ ПромСтройБанк

Павильон 45 на оптовом рынке

АКБ ИвестБанк

Павильон 45 на оптовом рынке

АКБ ПромСтройБанк

Бавария – фарфор

АКБ ИвестБанк

Бавария – фарфор

АКБ ПромСтройБанк

Фабрика джинсовой одежды

АКБ ИвестБанк

Фабрика джинсовой одежды

АКБ ПромСтройБанк

АКБ ПромСтройБанк

АКБ ИвестБанк

АКБ ПромСтройБанк

АКБ ПромСтройБанк

Результат запроса содержит комбинации всех контрагентов со всеми банками. Как правило, такой результат сам по себе смысла не имеет. Обычно комбинации записей из разных исходных таблиц требуется ограничить какими-либо условиями. В языке запросов имеется возможность описать такое соединение источников, указывая сами источники и определяя условия, в соответствии с которыми комбинации записей из этих источников требуется включить в результат запроса.

Соединения бывают нескольких видов; они описываются следующими правилами:

<Перечень соединений>

|

<Соединение> [<Перечень соединений>]

|

[ВНУТРЕННЕЕ] СОЕДИНЕНИЕ <Описание источника>
ПО <Условие отбора> |
ЛЕВОЕ [ВНЕШНЕЕ] СОЕДИНЕНИЕ <Описание источника>
ПО <Условие отбора> |
ПРАВОЕ [ВНЕШНЕЕ] СОЕДИНЕНИЕ <Описание источника>
ПО <Условие отбора> |
ПОЛНОЕ [ВНЕШНЕЕ] СОЕДИНЕНИЕ <Описание источника>
ПО <Условие отбора>

В общем случае <Перечень соединений> может содержать и описывать не только одно соединение (двух источников), но и несколько соединений нескольких источников сразу.

<Описание источника> содержит описание исходной таблицы.

<Условие отбора> содержит условия, в соответствии с которыми в выборке необходимо соединить данные из исходных таблиц – источников запроса.

Ключевые слова ЛЕВОЕ, ПРАВОЕ и ПОЛНОЕ уточняют характер соединения. Слова ВНУТРЕННЕЕ или ВНЕШНЕЕ можно не указывать вообще, они повышают наглядность и удобочитаемость текста запроса.

Соединяемые источники не равнозначны между собой, и в некоторых случаях результат зависит от того, какая таблица указана первой, до ключевого слова СОЕДИНЕНИЕ (слева от него), а какая – второй (справа).

Практические примеры

Сверить два своих списка: внутреннее, левое, правое и полное соединение

Слева коды 1,2,3; справа 1,2,4. В пяти законченных запросах сравниваются совпадения, отсутствующие стороны и ошибка соединения без условия: вместо 2 пар получается 9.

Где и как запускать

Полный серверный общий модуль учебной базы 8.5.1.1343. Свои значения и приватные временные таблицы, без рабочих источников, внешних файлов и сети; здесь не запускался.

Подготовка

  1. В отдельной учебной базе 8.5.1.1343 создайте общий модуль УчебныйЯзык: Сервер=Истина, Глобальный=Ложь, ВызовСервера=Ложь, Привилегированный=Ложь. Клиентские и внешнее соединение отключены; ПовторноеИспользованиеВозвращаемыхЗначений=НеИспользовать.

  2. Вставьте весь приведённый текст в этот общий модуль. ВыполнитьПример — экспортная точка входа; остальные процедуры и функции располагаются рядом, а не внутри неё.

  3. В своей внешней учебной обработке создайте управляемую форму, команду Выполнить и кнопку. Клиентский обработчик Выполнить(Команда) вызывает свою процедуру ВыполнитьНаСервере(); она имеет директиву &НаСервереБезКонтекста и одну строку УчебныйЯзык.ВыполнитьПример();. Это серверный вызов общего модуля, а не разрешение клиенту прямо обращаться к серверной библиотеке.

  4. Все товары, количества и строки придуманы в этом модуле. Запись документов, подключение к чужой базе, файлы, сеть и печать не используются. В рабочие общие модули текст не вставляйте.

  5. HTML только показывает BSL. Код в 1С не запускался; перечисленные результаты и отрицательные опыты проверяйте самостоятельно в собственном стенде.

  6. Два списка полностью создаются внутри модуля. Подключения к документам, регистрам и справочникам конфигурации не нужны.

  7. Функция СоздатьСвоюТаблицу получает только три заданных числовых кода от собственного модуля; массив создаётся пустым и заполняется Добавить, не конструктором размеров.

  8. Нужен серверный контекст по общей подготовке. Две временные таблицы создаются в новом собственном МенеджерВременныхТаблиц; его не берут из рабочего отчёта.

Процедура ВыполнитьПример() Экспорт
    Левые = СоздатьСвоюТаблицу(1, 2, 3);
    Правые = СоздатьСвоюТаблицу(1, 2, 4);
    Если Левые.Количество() <> 3 Или Правые.Количество() <> 3 Тогда
        ВызватьИсключение "Изменился собственный образец двух списков";
    КонецЕсли;
    Менеджер = Новый МенеджерВременныхТаблиц;
    Попытка
        Создание = Новый Запрос;
        Создание.МенеджерВременныхТаблиц = Менеджер;
        Создание.Текст = "ВЫБРАТЬ Левые.Код КАК Код
        |ПОМЕСТИТЬ ВТСобственныеЛевые
        |ИЗ &Левые КАК Левые;
        |
        |ВЫБРАТЬ Правые.Код КАК Код
        |ПОМЕСТИТЬ ВТСобственныеПравые
        |ИЗ &Правые КАК Правые";
        Создание.УстановитьПараметр("Левые", Левые);
        Создание.УстановитьПараметр("Правые", Правые);
        Создание.ВыполнитьПакет();
        Запрос = Новый Запрос;
        Запрос.МенеджерВременныхТаблиц = Менеджер;
        Запрос.Текст = "ВЫБРАТЬ
        |    ЕСТЬNULL(Левые.Код, 0) КАК КодСлева,
        |    ЕСТЬNULL(Правые.Код, 0) КАК КодСправа,
        |    ВЫБОР КОГДА Левые.Код ЕСТЬ NULL ТОГДА ИСТИНА ИНАЧЕ ЛОЖЬ КОНЕЦ КАК НетСлева,
        |    ВЫБОР КОГДА Правые.Код ЕСТЬ NULL ТОГДА ИСТИНА ИНАЧЕ ЛОЖЬ КОНЕЦ КАК НетСправа
        |ИЗ ВТСобственныеЛевые КАК Левые
        |ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТСобственныеПравые КАК Правые
        |ПО Левые.Код = Правые.Код
        |УПОРЯДОЧИТЬ ПО КодСлева, КодСправа";
        Результат = Запрос.Выполнить().Выгрузить();
        Сообщить("Внутреннее");
        ПроверитьСвоиПары(Результат, 2, "1:1|2:2|", 0, 0);
        Запрос.Текст = "ВЫБРАТЬ
        |    ЕСТЬNULL(Левые.Код, 0) КАК КодСлева,
        |    ЕСТЬNULL(Правые.Код, 0) КАК КодСправа,
        |    ВЫБОР КОГДА Левые.Код ЕСТЬ NULL ТОГДА ИСТИНА ИНАЧЕ ЛОЖЬ КОНЕЦ КАК НетСлева,
        |    ВЫБОР КОГДА Правые.Код ЕСТЬ NULL ТОГДА ИСТИНА ИНАЧЕ ЛОЖЬ КОНЕЦ КАК НетСправа
        |ИЗ ВТСобственныеЛевые КАК Левые
        |ЛЕВОЕ ВНЕШНЕЕ СОЕДИНЕНИЕ ВТСобственныеПравые КАК Правые
        |ПО Левые.Код = Правые.Код
        |УПОРЯДОЧИТЬ ПО КодСлева, КодСправа";
        Результат = Запрос.Выполнить().Выгрузить();
        Сообщить("Левое");
        ПроверитьСвоиПары(Результат, 3, "1:1|2:2|3:0|", 0, 1);
        Запрос.Текст = "ВЫБРАТЬ
        |    ЕСТЬNULL(Левые.Код, 0) КАК КодСлева,
        |    ЕСТЬNULL(Правые.Код, 0) КАК КодСправа,
        |    ВЫБОР КОГДА Левые.Код ЕСТЬ NULL ТОГДА ИСТИНА ИНАЧЕ ЛОЖЬ КОНЕЦ КАК НетСлева,
        |    ВЫБОР КОГДА Правые.Код ЕСТЬ NULL ТОГДА ИСТИНА ИНАЧЕ ЛОЖЬ КОНЕЦ КАК НетСправа
        |ИЗ ВТСобственныеЛевые КАК Левые
        |ПРАВОЕ ВНЕШНЕЕ СОЕДИНЕНИЕ ВТСобственныеПравые КАК Правые
        |ПО Левые.Код = Правые.Код
        |УПОРЯДОЧИТЬ ПО КодСлева, КодСправа";
        Результат = Запрос.Выполнить().Выгрузить();
        Сообщить("Правое");
        ПроверитьСвоиПары(Результат, 3, "0:4|1:1|2:2|", 1, 0);
        Запрос.Текст = "ВЫБРАТЬ
        |    ЕСТЬNULL(Левые.Код, 0) КАК КодСлева,
        |    ЕСТЬNULL(Правые.Код, 0) КАК КодСправа,
        |    ВЫБОР КОГДА Левые.Код ЕСТЬ NULL ТОГДА ИСТИНА ИНАЧЕ ЛОЖЬ КОНЕЦ КАК НетСлева,
        |    ВЫБОР КОГДА Правые.Код ЕСТЬ NULL ТОГДА ИСТИНА ИНАЧЕ ЛОЖЬ КОНЕЦ КАК НетСправа
        |ИЗ ВТСобственныеЛевые КАК Левые
        |ПОЛНОЕ ВНЕШНЕЕ СОЕДИНЕНИЕ ВТСобственныеПравые КАК Правые
        |ПО Левые.Код = Правые.Код
        |УПОРЯДОЧИТЬ ПО КодСлева, КодСправа";
        Результат = Запрос.Выполнить().Выгрузить();
        Сообщить("Полное");
        ПроверитьСвоиПары(Результат, 4, "0:4|1:1|2:2|3:0|", 1, 1);
        Запрос.Текст = "ВЫБРАТЬ
        |    ЕСТЬNULL(Левые.Код, 0) КАК КодСлева,
        |    ЕСТЬNULL(Правые.Код, 0) КАК КодСправа,
        |    ВЫБОР КОГДА Левые.Код ЕСТЬ NULL ТОГДА ИСТИНА ИНАЧЕ ЛОЖЬ КОНЕЦ КАК НетСлева,
        |    ВЫБОР КОГДА Правые.Код ЕСТЬ NULL ТОГДА ИСТИНА ИНАЧЕ ЛОЖЬ КОНЕЦ КАК НетСправа
        |ИЗ ВТСобственныеЛевые КАК Левые, ВТСобственныеПравые КАК Правые
        |УПОРЯДОЧИТЬ ПО КодСлева, КодСправа";
        Результат = Запрос.Выполнить().Выгрузить();
        Сообщить("Без условия: декартово произведение");
        ПроверитьСвоиПары(Результат, 9, "1:1|1:2|1:4|2:1|2:2|2:4|3:1|3:2|3:4|", 0, 0);
    Исключение
        Менеджер.Закрыть();
        ВызватьИсключение;
    КонецПопытки;
    Менеджер.Закрыть();
КонецПроцедуры

Функция СоздатьСвоюТаблицу(Первый, Второй, Третий)
    Источник = Новый ТаблицаЗначений;
    Источник.Колонки.Добавить("Код", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(5, 0, ДопустимыйЗнак.Неотрицательный)));
    Коды = Новый Массив;
    Коды.Добавить(Первый);
    Коды.Добавить(Второй);
    Коды.Добавить(Третий);
    Для Каждого КодОбразца Из Коды Цикл
        Если КодОбразца <= 0 Тогда
            ВызватьИсключение "Ноль зарезервирован для отображения NULL, свои коды положительны";
        КонецЕсли;
        НоваяСтрока = Источник.Добавить();
        НоваяСтрока.Код = КодОбразца;
    КонецЦикла;
    Возврат Источник;
КонецФункции

Процедура ПроверитьСвоиПары(Таблица, ОжидаемоеКоличество, ОжидаемыеПары, ОжидалосьБезЛевой, ОжидалосьБезПравой)
    Если Таблица.Количество() <> ОжидаемоеКоличество Тогда
        ВызватьИсключение "Неверное количество собственных пар";
    КонецЕсли;
    Пары = "";
    БезЛевой = 0;
    БезПравой = 0;
    Для Каждого СтрокаРезультата Из Таблица Цикл
        Пары = Пары + Строка(СтрокаРезультата.КодСлева) + ":" + Строка(СтрокаРезультата.КодСправа) + "|";
        Если СтрокаРезультата.НетСлева Тогда
            БезЛевой = БезЛевой + 1;
        КонецЕсли;
        Если СтрокаРезультата.НетСправа Тогда
            БезПравой = БезПравой + 1;
        КонецЕсли;
    КонецЦикла;
    Если Пары <> ОжидаемыеПары Или БезЛевой <> ОжидалосьБезЛевой Или БезПравой <> ОжидалосьБезПравой Тогда
        ВызватьИсключение "Пары или стороны NULL не соответствуют своему образцу";
    КонецЕсли;
    Сообщить("Проверены пары: " + Пары);
    Сообщить("Без левой: " + Строка(БезЛевой) + "; без правой: " + Строка(БезПравой));
КонецПроцедуры

Разбор по шагам

  1. Создать две собственные типизированные таблицы: 1,2,3 и 1,2,4. Все коды положительны, каждый ключ встречается один раз.

  2. Передать таблицы двумя параметрами и выполнить пакет создания двух временных таблиц своего менеджера.

  3. Внутреннее соединение ПО равенству ключей даёт только 1:1 и 2:2, всего две пары.

  4. Левое сохраняет весь левый список: добавляется 3:NULL. Правое сохраняет правый: добавляется NULL:4.

  5. Полное сохраняет несовпавшие строки обеих сторон, всего четыре пары. NULL в полях означает отсутствие подходящей строки другой стороны.

  6. Для отображения заменить NULL на зарезервированный 0, но отдельно сохранить флаги ЕСТЬ NULL до замены. Настоящего кода 0 в собственных источниках нет.

  7. Две таблицы через запятую без условия дают все девять комбинаций 3×3, не только совпадения. Это отдельный контроль распространённой ошибки.

  8. Явно сортировать по двум выведенным кодам, затем проверить полный состав пар, их количество и обе стороны NULL.

  9. Закрыть свой менеджер временных таблиц при успехе и при исключении; рабочие таблицы и файлы не затрагиваются.

Как проверить результат

  1. Получены пять проверенных составов пар: 2,3,3,4 и 9 строк.

  2. У левого соединения одна отсутствующая правая сторона; у правого одна левая; у полного по одной с каждой стороны.

  3. Ожидаемые строки пар заданы полностью и проверены, а не только количество строк.

  4. Сортировка определена явно; менеджер закрыт в обоих исходах, записи конфигурации не менялись.

Ожидаемый результат

Пять видов сочетания собственных источников разобраны на одном понятном образце с проверками состава и NULL.

На что обратить внимание

  1. ЛЕВОЕ/ПРАВОЕ определяют расположение источников в тексте, не направление стрелки в произвольной картинке.

  2. NULL не является реальным нулевым кодом. Здесь 0 только отображаемый маркер; флаги отсутствия вычислены отдельно.

  3. Количество 2/3/3/4 верно для этих уникальных ключей. Повторяющиеся ключи размножают совпавшие пары; соединение само по себе не удаляет дубли.

  4. Отбор по полю правого источника в ГДЕ после левого соединения может исключить строки NULL. Этот урок не подменяет ПО таким отбором.

  5. У собственного менеджера временные таблицы приватны этому опыту; не передавайте сюда менеджер чужого рабочего запроса.

  6. HTML BSL не исполняет; проверки Python моделируют свой образец, но не подтверждают выполнение в 1С.

Пример не запускался в вашей базе. Перед применением проверьте его в указанном учебном контексте.

Не хотите разбираться сами?

Адаптируем это решение под вашу базу. Оценка бесплатно.

Оставить задачу

1С, 1С:Предприятие — товарные знаки ООО «1С». Сайт не является официальным представительством фирмы «1С» и не аффилирован с ней.