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