При написании запросов в системе «1С:Предприятие» одним из самых частых вопросов, возникающих у начинающих разработчиков, является выбор порядка таблиц в секции ЛЕВОЕ СОЕДИНЕНИЕ. На первый взгляд может показаться, что разницы нет, однако на практике перестановка таблиц местами может привести не только к неверному результату (количеству строк), но и к значительным задержкам в работе системы. В рамках данной статьи мы подробно разберем, как определить «главную» таблицу, как работают механизмы сопоставления данных и какие «подводные камни» подстерегают программиста при работе с соединениями.
В языке запросов 1С, как и в классическом SQL, под «главной» понимается таблица, данные из которой должны попасть в результирующую выборку в полном объеме, независимо от того, нашлось ли для них соответствие в другой таблице. Рассмотрим ключевые правила определения такой таблицы:
ЛЕВОЕ СОЕДИНЕНИЕ.ПО.Проанализируем классический пример: нам нужно получить список всех товаров из справочника Номенклатура и узнать их текущие остатки. Если мы поставим справочник слева, а таблицу остатков справа, мы получим список всех товаров (даже тех, которых нет на складе). Если же мы сделаем наоборот, то в результат попадут только те товары, по которым есть остатки, что фактически превратит наш запрос в аналог ВНУТРЕННЕГО СОЕДИНЕНИЯ.
Рассмотрим ситуацию, описанную в обсуждении: у нас есть документ и регистр сведений (РС). Что выбрать в качестве главной таблицы? Ответ всегда зависит от цели запроса. Давайте проанализируем две разные задачи.
Задача №1: Вывести список строк табличной части документа и для каждой строки найти значение из регистра (например, цену или скидку из РС ЦеныНоменклатуры).
В этом случае главной таблицей будет выступать табличная часть документа. Нам важно увидеть все строки документа, даже если в регистре цен не заполнена информация для какого-то товара. Посмотрим на пример кода:
ВЫБРАТЬ
ДокТовары.Номенклатура КАК Товар,
ДокТовары.Количество КАК Количество,
ЕСТЬNULL(Цены.Цена, 0) КАК Цена
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК ДокТовары
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Период, ) КАК Цены
ПО ДокТовары.Номенклатура = Цены.Номенклатура
ГДЕ
ДокТовары.Ссылка = &Ссылка
Задача №2: Проанализировать записи регистра сведений и найти, каким документом была сделана запись (если такая связь предусмотрена логикой базы).
Здесь главной таблицей станет уже Регистр Сведений, так как нас интересует полнота данных именно в нем.
Разберем критическую ошибку, которую часто допускают программисты. При использовании левого соединения крайне важно следить за тем, где накладываются условия на правую (присоединяемую) таблицу. Рассмотрим ситуацию: мы соединяем Контрагентов (слева) и Договоры (справа). Мы хотим получить всех контрагентов, но только те договоры, которые помечены как «Действующие».
Если мы напишем условие в секции ГДЕ, например ГДЕ Договоры.Статус = "Действующий", то произойдет следующее: СУБД сначала выполнит левое соединение (у контрагентов без договоров в поле статус будет NULL), а затем применит фильтр ГДЕ. Так как NULL не равен строке "Действующий", все контрагенты без договоров будут отсечены. Наш запрос превратится во внутреннее соединение.
Правильное решение: Условия на правую таблицу нужно переносить в секцию ПО. Выясним причину: условия внутри ПО определяют, какие записи присоединить, а условия в ГДЕ определяют, какие записи оставить в итоговом результате.
// Правильный подход
ВЫБРАТЬ
Контрагенты.Наименование,
Договоры.Номер
ИЗ
Справочник.Контрагенты КАК Контрагенты
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Договоры КАК Договоры
ПО Контрагенты.Ссылка = Договоры.Владелец
И Договоры.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыДоговоров.Действует)
Важно помнить, что выбор главной таблицы влияет не только на наличие NULL, но и на количество строк. Если в левой (главной) таблице 1 строка, а в правой таблице по условию связи найдено 10 строк, то в результате вы получите 10 строк. Левая таблица «размножится».
Проанализируем ситуацию: вы хотите проверить, есть ли у документа хотя бы одна строка с услугой. Если вы просто присоедините таблицу Товары левым соединением, и в документе будет 50 строк, ваш документ задублируется 50 раз. В таких случаях эффективнее использовать конструкцию ГДЕ Ссылка В (ВЫБРАТЬ...) или соединение с предварительно сгруппированной таблицей.
Хотя логически мы определяем главную таблицу по смыслу задачи, для СУБД (SQL Server или PostgreSQL) порядок таблиц также имеет значение. Посмотрим на ситуацию с точки зрения оптимизатора:
ПО), обязательно должны быть проиндексированы. В 1С это обычно поля типа Ссылка, Период или измерения регистров, которые индексируются платформой автоматически.Современный стандарт разработки на платформе 1С 8.3 предполагает активное использование временных таблиц. Рассмотрим, как это помогает решить проблему выбора главной таблицы. Вместо того чтобы строить сложные цепочки из 10 соединений, мы можем:
Это делает структуру запроса линейной и понятной: «Берем наш отобранный список и последовательно дополняем его данными из разных источников».
// Шаг 1: Подготовка фундамента
ВЫБРАТЬ Ссылка ИЗ Документ.ЗаказПокупателя ГДЕ Дата МЕЖДУ &Начало И &Конец ПОМЕСТИТЬ ВТ_Заказы;
ИНДЕКСИРОВАТЬ ВТ_Заказы.Ссылка;
// Шаг 2: Получение данных
ВЫБРАТЬ
ВТ.Ссылка,
ЕСТЬNULL(Остатки.КоличествоОстаток, 0) КАК Остаток
ИЗ
ВТ_Заказы КАК ВТ
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки(, Номенклатура В (ВЫБРАТЬ Номенклатура ИЗ Документ.ЗаказПокупателя.Товары ГДЕ Ссылка В (ВЫБРАТЬ Ссылка ИЗ ВТ_Заказы))) КАК Остатки
ПО ВТ.Ссылка = ... // и так далее
При левом соединении для записей, которые не были найдены в правой таблице, СУБД возвращает значение NULL. Это «пустое» значение коварно: любая математическая операция с ним (например, Цена * NULL) даст в результате NULL. Чтобы этого избежать, всегда используйте функцию ЕСТЬNULL(Поле, ЗначениеЗамены).
Проанализируем: если мы выводим сумму, логично заменить NULL на 0. Если выводим строку — на пустую строку. Это обеспечит корректную работу отчетов и исключит странные пустые ячейки в результатах запроса.
Таким образом, определение главной таблицы — это не просто выбор «кто слева», а осознанное проектирование структуры данных, которое учитывает и требования бизнеса к полноте отчета, и технические особенности работы баз данных.