Как работают оператор ПЕРВЫЕ и соединения таблиц в запросах с ОБЪЕДИНИТЬ

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

При работе с языком запросов 1С программисты часто сталкиваются с неочевидным поведением стандартных конструкций, особенно когда речь идет об объединении данных из нескольких источников (например, нескольких табличных частей разных документов). В данной статье мы подробно разберем два критически важных вопроса: как оператор ПЕРВЫЕ ведет себя внутри конструкции ОБЪЕДИНИТЬ и как правильно оптимизировать соединения этих данных с регистрами сведений для обеспечения максимальной производительности системы.

Особенности работы оператора ПЕРВЫЕ в объединениях

Рассмотрим ситуацию, когда нам необходимо выбрать ограниченное количество записей из результата объединения нескольких запросов. Распространенной ошибкой является мнение, что указание ПЕРВЫЕ в начале запроса ограничит общий результат всей выборки. На самом деле, в языке запросов 1С (и в лежащем в его основе T-SQL) оператор ПЕРВЫЕ имеет локальную область видимости.

Проанализируем это на примере. Допустим, у нас есть две табличные части, и в каждой из них более 100 строк. Мы пишем следующий запрос:


ВЫБРАТЬ ПЕРВЫЕ 100
    ТЧ1.Ссылка КАК Документ,
    ТЧ1.Номенклатура КАК Товар
ИЗ
    Документ.РеализацияТоваровУслуг.Товары КАК ТЧ1

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ ПЕРВЫЕ 100
    ТЧ2.Ссылка,
    ТЧ2.Номенклатура
ИЗ
    Документ.АктВыполненныхРабот.Услуги КАК ТЧ2

В данном случае мы получим не 100 записей, а до 200 записей. Это происходит потому, что инструкция ПЕРВЫЕ 100 отрабатывает независимо для каждой части объединения. Если в первой таблице есть 100 строк, они будут выбраны. Если во второй есть еще 100, они также попадут в результат.

Если же ваша задача — получить строго 100 записей из общего массива данных, нам необходимо использовать механизм вложенных запросов или временных таблиц. Рассмотрим, как это сделать правильно:


ВЫБРАТЬ ПЕРВЫЕ 100
    ВложенныйЗапрос.Документ,
    ВложенныйЗапрос.Товар
ИЗ
    (ВЫБРАТЬ
        ТЧ1.Ссылка КАК Документ,
        ТЧ1.Номенклатура КАК Товар
    ИЗ
        Документ.РеализацияТоваровУслуг.Товары КАК ТЧ1

    ОБЪЕДИНИТЬ ВСЕ

    ВЫБРАТЬ
        ТЧ2.Ссылка,
        ТЧ2.Номенклатура
    ИЗ
        Документ.АктВыполненныхРабот.Услуги КАК ТЧ2) КАК ВложенныйЗапрос
УПОРЯДОЧИТЬ ПО
    ВложенныйЗапрос.Документ

Важное замечание: Использование ПЕРВЫЕ без УПОРЯДОЧИТЬ ПО делает результат запроса недетерминированным. Это означает, что СУБД вернет те записи, которые первыми попали в выборку согласно текущему состоянию индексов и статистики. Для получения предсказуемого результата всегда указывайте сортировку.

Оптимизация соединений: в каждой части или с итоговой таблицей?

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

Вариант 1: Соединение внутри каждой части объединения

Этот подход предпочтителен в случаях, когда соединение выступает в роли фильтра. Например, если вы используете ВНУТРЕННЕЕ СОЕДИНЕНИЕ (INNER JOIN) или накладываете условие в секции ГДЕ на данные из регистра.

Разберем почему: если мы сначала объединим 10 000 строк из ТЧ документов во временную таблицу, а потом начнем их фильтровать соединением с РС, нам придется записать все 10 000 строк на диск (в базу tempdb). Если же мы сделаем соединение внутри каждой части запроса, то в результат (и во временную таблицу) попадут только те строки, которые прошли фильтр. Это значительно сокращает объем дисковых операций.


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

ОБЪЕДИНИТЬ ВСЕ
// ... аналогично для других ТЧ

Вариант 2: Соединение с итоговой временной таблицей

Этот метод эффективнее, когда вам нужно просто "дотянуть" справочные данные (например, артикул номенклатуры или текущую цену) без фильтрации строк. Рассмотрим алгоритм действий:

  1. Сначала мы собираем данные из всех ТЧ через ОБЪЕДИНИТЬ ВСЕ и помещаем их во временную таблицу (ВТ).
  2. Критически важный шаг: индексируем временную таблицу по полям связи.
  3. Соединяем проиндексированную ВТ с целевым регистром или таблицей.

Рассмотрим пример реализации:


// Помещаем данные во временную таблицу
ВЫБРАТЬ
    ТЧ1.Номенклатура КАК Номенклатура,
    ТЧ1.Количество КАК Количество
ПОМЕСТИТЬ ВТ_Данные
ИЗ
    Документ.РеализацияТоваровУслуг.Товары КАК ТЧ1

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    ТЧ2.Номенклатура,
    ТЧ2.Количество
ИЗ
    Документ.ПеремещениеТоваров.Товары КАК ТЧ2
;

// Индексируем для быстрого соединения
ИНДЕКСИРОВАТЬ ПО
    Номенклатура
;

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

Сравнение подходов и нюансы

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

Также стоит обратить внимание на типизацию полей. При объединении ТЧ разных документов 1С может привести поля к составному типу. Соединение по составному типу (особенно если одна из частей — ссылка на документ, а другая — на справочник) крайне негативно сказывается на производительности. В таких случаях помещение данных в ВТ с явным приведением типа через ВЫРАЗИТЬ является обязательным условием оптимизации.

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

Итоговые рекомендации

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

Соблюдение этих принципов позволит вам создавать быстрые и масштабируемые решения даже при работе с очень большими объемами данных в системе 1С:Предприятие.

← На главную