Почему запрос в 1С возвращает больше строк, чем ожидалось, и как работает оптимизатор соединений

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При работе с запросами в 1С программисты часто сталкиваются с ситуацией, когда результат выполнения ЛЕВОЕ СОЕДИНЕНИЕ (LEFT JOIN) кажется нелогичным. Например, в левой таблице 8 строк, в правой — 2, а на выходе получается 16. В такой ситуации может возникнуть ощущение, что платформа выполняет «какой-то другой» запрос или превращает ЛЕВОЕ СОЕДИНЕНИЕ в ПОЛНОЕ. На самом деле, за этим поведением стоят строгие правила реляционной алгебры и механизмы оптимизации платформы 1С:Предприятие.

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

Разберем механизм размножения строк при соединении

Первое и самое важное правило: любое соединение таблиц (будь то ЛЕВОЕ, ВНУТРЕННЕЕ или ПОЛНОЕ) концептуально является декартовым произведением строк с последующей фильтрацией по условию связи. Если в правой таблице по ключам связи находится более одной записи для строки из левой таблицы, происходит «размножение» строк.

Рассмотрим ситуацию автора темы. В левой таблице (ТоварыНаСервере) — 8 строк. В правой таблице (Товары) — всего 2 строки. Казалось бы, результат левого соединения не должен превышать 8 строк. Но если для каждой из 8 строк левой таблицы в правой таблице нашлось 2 соответствия (например, по номенклатуре и характеристике), то каждая исходная строка продублируется. В итоге мы получим 8 * 2 = 16 строк.

Проанализируем пример:

Представьте, что в ТоварыНаСервере есть строка «Печенье «Юбилейное», а в таблице Товары (которая передана как параметр) по какой-то причине эта номенклатура встречается дважды (например, с разными сериями, которые не указаны в условии связи ПО). SQL-сервер обязан выдать все возможные комбинации, подходящие под условие. Именно поэтому результат в 16 строк — это не признак ПОЛНОГО СОЕДИНЕНИЯ, а признак наличия дублей в ключевых полях правой таблицы.

Выясним причину странного поведения оптимизатора запросов

В современных версиях платформы 1С 8.3 реализован агрессивный оптимизатор. Если в тексте запроса вы описываете ЛЕВОЕ СОЕДИНЕНИЕ, но при этом:

  1. Ни одно поле из правой таблицы не выбирается в секции ВЫБРАТЬ.
  2. Правая таблица не используется в условиях ГДЕ или других частях запроса.

Платформа или СУБД могут попытаться исключить это соединение из плана выполнения, так как оно «бесполезно» с точки зрения состава данных. Однако, если соединение влияет на количество строк (как в случае с дублями), оптимизатор 1С обычно оставляет его, чтобы не исказить результат. Но стоит вам изменить структуру запроса, например, явно перечислить поля вместо *, как логика построения плана может измениться.

Рассмотрим код, который вызывает вопросы:


ВЫБРАТЬ
    ТоварыНаСервере.Номенклатура КАК Номенклатура,
    ТоварыНаСервере.Характеристика КАК Характеристика,
    ...
ИЗ
    ТоварыНаСервере КАК ТоварыНаСервере
        ЛЕВОЕ СОЕДИНЕНИЕ Товары КАК Товары
        ПО ТоварыНаСервере.Номенклатура = Товары.Номенклатура
            И ТоварыНаСервере.Характеристика = Товары.Характеристика

В данном фрагменте из таблицы Товары не выбирается ни одного поля. Если ваша цель — просто проверить наличие записей в правой таблице, правильнее использовать ГДЕ Номенклатура В (ВЫБРАТЬ Номенклатура ИЗ...) или ГДЕ СУЩЕСТВУЕТ (ВЫБРАТЬ 1 ИЗ...). Использование ЛЕВОЕ СОЕДИНЕНИЕ «впустую» — это плохая практика, которая нагружает СУБД лишними операциями поиска совпадений.

Куда пропадают временные таблицы

Автор заметил, что при определенных условиях временная таблица Товары исчезает из МенеджерВременныхТаблиц. Это происходит по следующим причинам:

1. Оптимизация параметров-таблиц. Когда мы передаем Таблицу Значений в запрос через параметр (&Товары), 1С создает реальную временную таблицу в СУБД. Если оптимизатор видит, что эта таблица далее не используется (например, вы убрали из финальной выборки поля ЕСТЬNULL(..., Товары.Количество)), он может просто проигнорировать её создание. Если таблица не была создана физически, её не будет и в менеджере.

2. Использование «Звездочки» (.*). Использование конструкции ВЫБРАТЬ * ПОМЕСТИТЬ... крайне не рекомендуется. При передаче ТЗ через параметр 1С должна определить типы колонок. Если колонки ТЗ не типизированы строго (содержат Произвольный тип), платформа создает в СУБД максимально «широкие» поля. Это мешает индексации и делает соединение крайне медленным, а иногда приводит к ошибкам при сопоставлении типов в JOIN.

Как правильно работать с соединениями и временными таблицами

Чтобы запрос работал предсказуемо и быстро, рекомендуем придерживаться следующих правил:

1. Всегда типизируйте Таблицы Значений

Перед передачей ТЗ в запрос убедитесь, что у колонок заданы конкретные типы. Это позволит платформе создать оптимальную структуру временной таблицы в SQL.

2. Используйте индексацию

Если вы планируете соединять временную таблицу с другими данными, её обязательно нужно проиндексировать по полям связи. Без этого СУБД будет вынуждена перебирать все строки (Table Scan), что приведет к деградации производительности.


ВЫБРАТЬ
    Товары.Номенклатура,
    Товары.Характеристика,
    Товары.Количество
ПОМЕСТИТЬ ВТ_Товары
ИЗ
    &Товары КАК Товары
ИНДЕКСИРОВАТЬ ПО
    Номенклатура,
    Характеристика
;

3. Избегайте лишних соединений

Если вам нужно поле из правой таблицы только в случае его наличия, используйте ЕСТЬNULL. Но если вы обнаружили, что строк стало больше, чем в исходной таблице, проверьте правую таблицу на наличие дублей. Если дубли допустимы по бизнес-логике, используйте агрегатные функции (СУММА, МАКСИМУМ) и СГРУППИРОВАТЬ ПО перед тем, как соединять таблицы.

4. Проверка через Консоль запросов

Для отладки подобных ситуаций лучше всего использовать консоль запросов с возможностью просмотра временных таблиц. Если результат запроса в коде отличается от консоли — проверьте состав и типы данных в передаваемой Таблице Значений. Часто проблема кроется не в тексте запроса, а в «мусорных» данных внутри параметра-таблицы.

Резюмируем: «Лишние» строки в ЛЕВОМ СОЕДИНЕНИИ — это всегда следствие дублей в правой таблице по полям связи. Платформа 1С лишь транслирует ваш запрос в SQL, и если соединение описано, оно будет выполнено согласно правилам работы с реляционными данными, даже если это приведет к геометрическому росту количества строк.

← На главную