Как эффективнее выбрать данные из шапки и табличной части справочника или документа в 1С?

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

При разработке отчетов и обработок в 1С программисты часто сталкиваются с дилеммой: как получить данные из шапки справочника (или документа) и его табличной части максимально быстро. Существует два основных подхода: использование одного сложного запроса с соединениями или получение ссылки на объект с последующим обращением к табличной части через точку. Рассмотрим подробно, почему классическая рекомендация «всегда использовать соединения» иногда дает неожиданные результаты по скорости, и разберем, как правильно оптимизировать такие запросы.

Анализ проблемы: почему соединение может работать медленнее?

Рассмотрим ситуацию, с которой столкнулся автор темы. При выполнении запроса в файловом варианте базы данных использование ЛЕВОЕ СОЕДИНЕНИЕ с табличной частью показало худшие результаты, чем простое обращение к объекту в цикле. Давайте разберем по шагам, почему это происходит и какие подводные камни здесь скрыты.

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


ВЫБРАТЬ
    МойСправочник.Ссылка,
    Фильтры.Субконто
ИЗ
    Справочник.МойСправочник КАК МойСправочник
ЛЕВОЕ СОЕДИНЕНИЕ (
    ВЫБРАТЬ
        Ссылка,
        Субконто
    ИЗ
        Справочник.МойСправочник.Фильтры
) КАК Фильтры ПО МойСправочник.Ссылка = Фильтры.Ссылка
ИТОГИ ПО Ссылка

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

Различие между файловым и клиент-серверным режимами

Важно понимать, что механизмы работы DBEngine (файловая база) и SQL Server существенно различаются. В файловом режиме оптимизатор запросов довольно примитивен. Когда вы обращаетесь к табличной части через точку в коде (Выборка.Ссылка.Фильтры.Выгрузить()), 1С выполняет очень простой и быстрый запрос по первичному ключу (индексу Ссылка). На небольших объемах данных цепочка таких быстрых точечных запросов может оказаться быстрее одного «тяжелого» соединения, которое требует построения плана и выделения памяти под результат соединения.

Однако в клиент-серверном режиме ситуация меняется. SQL-сервер гораздо лучше справляется с соединениями, но здесь вступает в силу другой фактор — сетевой трафик и объем данных. Если у вас в шапке справочника много реквизитов (например, длинные описания или ХранилищаЗначений), то при левом соединении с табличной частью из 100 строк, данные шапки будут продублированы 100 раз для каждой строки ТЧ. Это называется эффектом декартова произведения, и это может «забить» канал связи между сервером приложений и сервером БД.

Правильный подход к написанию запроса с соединением

Для оптимизации запроса необходимо отказаться от вложенных запросов и соединять таблицы напрямую по индексированным полям. Рассмотрим правильный пример кода:


ВЫБРАТЬ
    МойСправочник.Ссылка КАК Ссылка,
    МойСправочник.Код КАК Код,
    МойСправочник.Наименование КАК Наименование,
    Фильтры.Субконто КАК Субконто,
    Фильтры.Значение КАК Значение
ИЗ
    Справочник.МойСправочник КАК МойСправочник
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.МойСправочник.Фильтры КАК Фильтры
    ПО МойСправочник.Ссылка = Фильтры.Ссылка
ИТОГИ ПО
    Ссылка

В данном примере мы используем прямое соединение. Поле Ссылка в табличных частях всегда индексировано платформой по умолчанию, поэтому соединение будет происходить максимально быстро. Использование ИТОГИ ПО Ссылка позволяет удобно обходить результат в иерархическом цикле, получая данные шапки на верхнем уровне и строки ТЧ на нижнем.

Техника «через точку» внутри самого запроса

Многие разработчики забывают, что 1С позволяет обращаться к реквизитам через точку прямо в тексте запроса, и платформа сама транслирует это в оптимальные соединения. Посмотрим на этот прием:


ВЫБРАТЬ
    Фильтры.Ссылка КАК Ссылка,
    Фильтры.Ссылка.Код КАК КодШапки,
    Фильтры.Ссылка.Наименование КАК НаименованиеШапки,
    Фильтры.Субконто КАК Субконто
ИЗ
    Справочник.МойСправочник.Фильтры КАК Фильтры

Проанализируем эту ситуацию. Когда мы делаем запрос напрямую к таблице Справочник.МойСправочник.Фильтры, платформа автоматически понимает, что Фильтры.Ссылка — это внешний ключ к основной таблице. Обращение Фильтры.Ссылка.Код заставит систему создать неявное ЛЕВОЕ СОЕДИНЕНИЕ (или ВНУТРЕННЕЕ СОЕДИНЕНИЕ, в зависимости от контекста). Это часто работает быстрее, так как платформа сама строит оптимальный путь к данным.

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

Использование пакетных запросов как «золотой стандарт»

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

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

Пример такого пакета запросов:


ВЫБРАТЬ
    МойСправочник.Ссылка КАК Ссылка
ПОМЕСТИТЬ ВТ_Ссылки
ИЗ
    Справочник.МойСправочник КАК МойСправочник
ГДЕ
    МойСправочник.ПометкаУдаления = ЛОЖЬ;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    МойСправочник.Ссылка,
    МойСправочник.Код,
    МойСправочник.Наименование
ИЗ
    ВТ_Ссылки КАК ВТ_Ссылки
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.МойСправочник КАК МойСправочник
    ПО ВТ_Ссылки.Ссылка = МойСправочник.Ссылка;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Фильтры.Ссылка,
    Фильтры.Субконто,
    Фильтры.Значение
ИЗ
    ВТ_Ссылки КАК ВТ_Ссылки
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.МойСправочник.Фильтры КАК Фильтры
    ПО ВТ_Ссылки.Ссылка = Фильтры.Ссылка

Роль объектного кэша 1С

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

Выводы и рекомендации

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

← На главную