Как работает левое соединение 1с
Перейти к содержимому

Как работает левое соединение 1с

  • автор:

1С. Запросы. Левое соединение работает как внутреннее – урезаются данные из левой таблицы.

При попытке соединения в запросе двух таблиц в режиме «Левое соединение» иногда возникает ситуация, когда данные из левой таблицы выбираются не все, как должно быть, а только те, которые есть в правой таблице. То есть, получается «Внутреннее соединение».

Например, мы хотим выбрать все документы «Заказ клиента» за какой-то период и соединить каждый из них со своим значением дополнительного сведения «Вип заказ», которое имеет тип «Булево» и хранится в регистре сведений «Дополнительные сведения».

Нужно помнить, что в указанном регистре сведений находятся не все документы «Заказ клиента», а только те, у которых значение дополнительного сведения «Вип заказ» установлено в «Да».

Итак, пишем запрос:

Запрос = Новый Запрос; Запрос.Текст = "ВЫБРАТЬ | ЗаказыКлиентов.Ссылка КАК ЗаказКлиента, | ДополнительныеСведения.Значение КАК ЭтоВипЗаказ |ИЗ | Документ.ЗаказКлиента КАК ЗаказыКлиентов | ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ДополнительныеСведения КАК ДополнительныеСведения | ПО (ЗаказыКлиентов.Ссылка = ДополнительныеСведения.Объект) |ГДЕ | ДополнительныеСведения.Свойство = &СвойствоДопСведения"; Запрос.УстановитьПараметр("СвойствоДопСведения", ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("Вип заказ (Список заказов клиентов)"); Запрос.Выполнить(); 

В выборке этого запроса мы увидим результат не «Левого соединения», которое мы указали в тексте запроса, а «Внутреннего соединения». В выборке окажутся только те документы «Заказ клиента», которые присутствуют в правой таблице – «ДополнительныеСведения».

Чтобы избежать такую ситуацию, нужно использовать временную таблицу или вложенный запрос.

Решение с использованием временной таблицы:

Запрос = Новый Запрос; Запрос.Текст = "ВЫБРАТЬ | ДополнительныеСведения.Объект КАК ЗаказКлиента, | ДополнительныеСведения.Значение КАК ЭтоВипЗаказ |ПОМЕСТИТЬ ДополнительныеСведения |ИЗ | РегистрСведений.ДополнительныеСведения КАК ДополнительныеСведения |ГДЕ | ДополнительныеСведения.Свойство = &СвойствоДопСведения |; | |//////////////////////////////////////////////////////////////////////////////// |ВЫБРАТЬ | ЗаказыКлиентов.Ссылка КАК ЗаказКлиента, | ДополнительныеСведения.ЭтоВипЗаказ КАК ЭтоВипЗаказ |ИЗ | Документ.ЗаказКлиента КАК ЗаказыКлиентов | ЛЕВОЕ СОЕДИНЕНИЕ ДополнительныеСведения КАК ДополнительныеСведения | ПО ЗаказыКлиентов.Ссылка = ДополнительныеСведения.ЗаказКлиента"; Запрос.УстановитьПараметр("СвойствоДопСведения", ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("Вип заказ (Список заказов клиентов)"); Запрос.Выполнить(); 

Решение с использованием вложенного запроса:

Запрос = Новый Запрос; Запрос.Текст = "ВЫБРАТЬ | ЗаказыКлиентов.Ссылка КАК ЗаказКлиента, | ДополнительныеСведения.ЭтоВипЗаказ КАК ЭтоВипЗаказ |ИЗ | Документ.ЗаказКлиента КАК ЗаказыКлиентов | ЛЕВОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ | ДополнительныеСведения.Объект КАК ЗаказКлиента, | ДополнительныеСведения.Значение КАК ЭтоВипЗаказ | ИЗ | РегистрСведений.ДополнительныеСведения КАК ДополнительныеСведения | ГДЕ | ДополнительныеСведения.Свойство = &СвойствоДопСведения) КАК ДополнительныеСведения | ПО (ЗаказыКлиентов.Ссылка = ДополнительныеСведения.ЗаказКлиента)"; Запрос.УстановитьПараметр("СвойствоДопСведения", ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("Вип заказ (Список заказов клиентов)"); Запрос.Выполнить(); 

Оба эти варианты запросов дадут в итоге настоящее «Левое соединение»: в выборку попадут все документы «Заказ клиента» из левой таблицы и значения дополнительного сведения из правой таблицы для тех документов, у которых это сведение задано.

  • Запросы
  • Временные таблицы

Как работает левое соединение 1с

Вступайте в мою группу помощник программиста.
В ней мы обсуждаем программирование в 1С.

Оглавление (нажмите, чтобы раскрыть)

  • Как сделать внутреннее соединение таблиц
  • Как сделать левое внешнее соединение таблиц
  • Как сделать правое внешнее соединение таблиц
  • Как сделать полное внешнее соединение таблиц
  • Как сделать перекрестное соединение таблиц
  • Скачать и выполнить эти примеры на компьютере

Соединение в запросах в языке 1С 8.3, 8.2 (в примерах)

С уважением, Владимир Милькин (преподаватель школы 1С программистов и разработчик обновлятора).

Владимир Милькин

Как помочь сайту: расскажите (кнопки поделиться ниже) о нём своим друзьям и коллегам. Сделайте это один раз и вы внесете существенный вклад в развитие сайта. На сайте нет рекламы, но чем больше людей им пользуются, тем больше сил у меня для его поддержки.

Нажмите одну из кнопок, чтобы поделиться:

Ограничения на соединения с вложенными запросами и виртуальными таблицами

Область применения: управляемое приложение, мобильное приложение, обычное приложение.

1.1. При написании запросов не следует использовать соединения с вложенными запросами. Следует соединять друг с другом только объекты метаданных или временные таблицы. Если запрос использует соединения с вложенными запросами, то его следует переписать с использованием временных таблиц (не важно с какой стороны соединения находится вложенный запрос), кроме случая, когда вложенный запрос сканирует мало записей.

Если запрос содержит соединения с вложенными запросами, то это может привести к следующим негативным последствиям:

  • Крайне медленное выполнение запроса при слабой загрузке серверного оборудования. Замедление запроса может быть очень значительным (до нескольких порядков);
  • Нестабильная работа запроса. При некоторых условиях запрос может работать достаточно быстро, при других — очень медленно;
  • Значительная разница по времени выполнения запроса на разных СУБД;
  • Повышенная чувствительность запроса к актуальности и полноте статистик. Сразу после полного обновления статистик запрос может работать быстро, но через некоторое время опять замедлиться.

Пример потенциально опасного запроса, использующего соединение с вложенным запросом:

ВЫБРАТЬ .
ИЗ Документ.РеализацияТоваровУслуг
ЛЕВОЕ СОЕДИНЕНИЕ (
ВЫБРАТЬ ИЗ РегистрСведений.Лимиты
ГДЕ .
СГРУППИРОВАТЬ ПО .
) ПО .

Оптимизатор сервера СУБД (независимо от того, какую СУБД вы используете) не всегда может правильно оптимизировать подобный запрос. В данном случае, проблемой для оптимизатора является выбор правильного способа соединения. Существуют несколько алгоритмов соединения двух выборок. Выбор того или иного алгоритма зависит от того, сколько записей будет содержаться в одной и в другой выборке. В том случае, если вы соединяете две физические таблицы, СУБД может легко определить объем обоих выборок на основании имеющейся статистики. Если же одна из соединяемых выборок представляет собой вложенный запрос, то понять, какое количество записей она вернет, становится очень сложно. В этом случае СУБД может ошибиться с выбором плана, что приведет к катастрофическому падению производительности запроса.

1.2. Для вышеприведенного примера получится следующий пакетный запрос:

// Создать менеджер временных таблиц
МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;
// Текст пакетного запроса
Запрос.Текст

Переписывание запроса по приведенной выше методике имеет своей целью упростить работу оптимизатору СУБД. В переписанном запросе все выборки, участвующие в соединениях будут представлять собой физические таблицы, и СУБД сможет легко определить размер каждой выборки. Это позволит СУБД гарантированно выбрать самый быстрый из всех возможных планов. Причем, СУБД будет делать правильный выбор независимо ни от каких условий. Переписанный подобным образом запрос будет работать одинаково хорошо на любых СУБД, что особенно важно при разработке тиражных решений. Кроме того, переписанный подобным образом запрос лучше читается, проще для понимания и отладки.

2. Если в запросе используется соединение с виртуальной таблицей языка запросов 1С:Предприятия (например, РегистрНакопления.Товары.Остатки ) и запрос работает с неудовлетворительной производительностью, то рекомендуется вынести обращение к виртуальной таблице в отдельный запрос с сохранением результатов во временной таблице (см. пункт 1.1).

3. Следует избегать неявных подзапросов, которые получаются при использовании вложенных соединений:

ВЫБРАТЬ .
ИЗ Справочник.Номенклатура
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыОрганизаций
ПО .
ПО .

Проблема в том, что, по сути, этот запрос аналогичен следующему:

ВЫБРАТЬ .
ИЗ Справочник.Номенклатура
ЛЕВОЕ СОЕДИНЕНИЕ (
ВЫБРАТЬ .
ИЗ РегистрНакопления.ТоварыНаСкладах
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыОрганизаций
ПО . )
ПО .

Вместо вложенных соединений, как показано выше, следует использовать последовательные соединения:

ВЫБРАТЬ .
ИЗ Справочник.Номенклатура
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах
ПО .
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыОрганизаций
ПО .

При этом следует понимать, что вложенные и последовательные соединения – это разные запросы, которые могут дать разный результат.

Если вложенное соединение использовано из предположения, что оно аналогично последовательному соединению, то следует просто переписать его на последовательное соединение.

Если вложенное соединение делается осмысленно, то от него следует отказаться, т.к. оно может существенно снизить производительность, как и соединение с подзапросом. Как и в случае с подзапросом, такое соединение можно заменить на соединение с временной таблицей, но лучше вначале подумать, как заменить его на последовательное соединение, т.к. оно будет работать эффективнее временной таблицы.

См. также

  • Использование вложенных запросов в условии соединения
  • Использование временных таблиц

Соединение таблиц в запросах 1С

В предыдущей статье был рассмотрен вопрос чем отличается соединение таблиц от объединения. А сейчас рассмотрим подробнее соединение таблиц.
Напомню, что видов соединений в языке запросов 1С8 может быть несколько, а именно: ЛЕВОЕ СОЕДИНЕНИЕ, ПРАВОЕ СОЕДИНЕНИЕ, ВНУТРЕННЕЕ СОЕДИНЕНИЕ, ПОЛНОЕ СОЕДИНЕНИЕ. Рассмотрим каждое из них подробно.

ЛЕВОЕ СОЕДИНЕНИЕ

В случае левого соединения данные из одной таблицы выбираются полностью, а из таблицы, которая присоединяется справа, выбираются только те записи для которых выполняется одно или несколько условий по которым эти таблицы соединяются. Из этого предложения конечно же мало, что можно понять, поэтому разберем пример.
Есть таблица «Товары»:

КодТовара Наименование
001 Яблоки
002 Апельсины
003 Мандарины

Сформируем эту таблицу в консоли запросов 1С с помощью вот такого запроса:

 ВЫБРАТЬ "001" КАК КодТовара, "Яблоки" КАК Наименование ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ "002", "Апельсины" ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ "003", "Мандарины" 

А еще есть таблица «Страны» вот такого вида:

КодТовара Страна
001 Россия
002 Турция
003 Марокко

Также сконструируем ее запросом:

 ВЫБРАТЬ "001" КАК КодТовара, "Россия" КАК Страна ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ "002", "Турция" ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ "003", "Марокко" 

А теперь соединим эти две таблицы с помощью левого соединения, чтобы получить одну, в которой будет и код товара, и наименование, и страна. Для этого поместим наши таблицы во временные и в пакетном запросе выполним левое соединение.
Записи будем связывать конечно же по полям КодТовара

 ВЫБРАТЬ "001" КАК КодТовара, "Яблоки" КАК Наименование ПОМЕСТИТЬ ВТ_Товар ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ "002", "Апельсины" ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ "003", "Мандарины" ; //////////////////////////////////////////////////////////////////////////////// ВЫБРАТЬ "001" КАК КодТовара, "Россия" КАК Страна ПОМЕСТИТЬ ВТ_Страна ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ "002", "Турция" ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ "003", "Марокко" ; //////////////////////////////////////////////////////////////////////////////// ВЫБРАТЬ ВТ_Товар.КодТовара, ВТ_Товар.Наименование, ВТ_Страна.Страна ИЗ ВТ_Товар КАК ВТ_Товар ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Страна КАК ВТ_Страна ПО ВТ_Товар.КодТовара = ВТ_Страна.КодТовара 

Можете скопировать этот код в консоль запросов и выполнив его получите вот такой результат:

КодТовара Наименование Страна
001 Яблоки Россия
002 Апельсины Турция
003 Мандарины Марокко

Здесь мы рассмотрели идеальную ситуацию, когда каждой записи в левой таблице соответствует одна запись в правой. Давайте немного усложним.
Допустим для нашего товара у нас есть еще цены. Причем они могут меняться в зависимости от даты:

КодТовара Дата Цена
001 01.01.2016 120
001 01.02.2016 150
002 01.01.2016 200
004 01.01.2016 350

Здесь мы видим, что для товара с кодом 001 (Яблоки) у нас 2 записи в таблице цен. Для мандаринов цена отсутствует. И кроме того в ценах есть код товара 004, которого нет в таблице с товарами. Давайте выполним левое соединение для товаров и цен с условием связи по полю код и посмотрим, что у нас получится.

 ВЫБРАТЬ "001" КАК КодТовара, "Яблоки" КАК Наименование ПОМЕСТИТЬ ВТ_Товар ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ "002", "Апельсины" ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ "003", "Мандарины" ; //////////////////////////////////////////////////////////////////////////////// ВЫБРАТЬ "001" КАК КодТовара, "01.01.2016" КАК Дата, 120 КАК Цена ПОМЕСТИТЬ ВТ_Цена ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ "001", "01.02.2016", 150 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ "002", "01.01.2016", 200 ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ "004", "01.01.2016", 350 ; //////////////////////////////////////////////////////////////////////////////// ВЫБРАТЬ ВТ_Товар.КодТовара, ВТ_Товар.Наименование, ВТ_Цена.Цена, ВТ_Цена.Дата ИЗ ВТ_Товар КАК ВТ_Товар ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Цена КАК ВТ_Цена ПО ВТ_Товар.КодТовара = ВТ_Цена.КодТовара 

После выполнения запроса видим вот такую картину:

КодТовара Наименование Цена Дата
001 Яблоки 120 01.01.2016
001 Яблоки 150 01.02.2016
002 Апельсины 200 01.01.2016
003 Мандарины NULL NULL

ПРАВОЕ СОЕДИНЕНИЕ

ВНУТРЕННЕЕ СОЕДИНЕНИЕ

В случае внутреннего соединения в итоговую таблицу попадут только те записи как из левой, так и из правой таблицы, для которых выполняется условие соединения. Если в нашем последнем запросе мы заменим строку

 ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Цена КАК ВТ_Цена 
 ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Цена КАК ВТ_Цена 

то в итоге получим:

КодТовара Наименование Цена Дата
001 Яблоки 120 01.01.2016
001 Яблоки 150 01.02.2016
002 Апельсины 200 01.01.2016

ПОЛНОЕ СОЕДИНЕНИЕ

В случае полного соединения в итоговую таблицу попадут все записи из обоих таблиц.
В нашем случае при использовании строки соединения

 ПОЛНОЕ СОЕДИНЕНИЕ ВТ_Цена КАК ВТ_Цена 

получим следующий результат

КодТовара Наименование Цена Дата
001 Яблоки 120 01.01.2016
001 Яблоки 150 01.02.2016
002 Апельсины 200 01.01.2016
003 Мандарины NULL NULL
NULL NULL 350 01.01.2016

Для тех кто только начинает работать с запросами 1С8 советую посмотреть как разные виды соединений отображаются в конструкторе запросов на закладке «Связи».

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *