В практике разработчика 1С часто встречается ситуация, когда внешне простой запрос, выбирающий данные из пары табличных частей документа, начинает выполняться недопустимо долго. В нашем примере запрос по документам ЧекККМ в конфигурации УТ 11.4 возвращал всего 227 записей, но время выполнения составляло более 50 секунд. Разберем детально, почему так происходит и как превратить медленный запрос в эффективный механизм.
Рассмотрим исходный запрос, который стал причиной "тормозов":
ВЫБРАТЬ
ЧекККМТовары.Номенклатура КАК Номенклатура,
ЧекККМТовары.СуммаАвтоматическойСкидки КАК Скидка,
ЧекККМСкидкиНаценки.СкидкаНаценка КАК СкидкаНаценка,
ЧекККМТовары.Продавец КАК Продавец,
ЧекККМТовары.Ссылка.Склад КАК Склад
ИЗ
Документ.ЧекККМ.Товары КАК ЧекККМТовары
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.ЧекККМ.СкидкиНаценки КАК ЧекККМСкидкиНаценки
ПО ЧекККМТовары.Ссылка = ЧекККМСкидкиНаценки.Ссылка
И ЧекККМТовары.КлючСвязи = ЧекККМСкидкиНаценки.КлючСвязи
ГДЕ
ЧекККМТовары.Ссылка.Дата >= &ДатаНачала
И ЧекККМТовары.Ссылка.Дата <= &ДатаОкончания
Проанализируем ситуацию. На первый взгляд запрос кажется логичным, но он содержит критические архитектурные недостатки для платформы 1С и SQL-сервера:
ЧекККМТовары.Ссылка.Дата и ЧекККМТовары.Ссылка.Склад заставляет систему выполнять неявные соединения с основной таблицей документа. Если в запросе много таких обращений, SQL-сервер строит сложный план с множеством лишних JOIN.Товары и СкидкиНаценки) еще до того, как сработает условие ГДЕ.ЧекККМ часто наложены ограничения доступа. При обращении к полям через Ссылка система добавляет к вашему коду массивные подзапросы для проверки прав, что замедляет работу в геометрической прогрессии.Первое и самое важное правило оптимизации — сначала отсекаем лишнее. Вместо того чтобы соединять все товары со всеми скидками за всю историю базы, мы должны сначала получить список ссылок на документы, входящие в нужный период. Рассмотрим, как это сделать с помощью временной таблицы.
Создадим временную таблицу ВТ_ЧекККМ, где выберем только нужные нам ссылки и необходимые реквизиты шапки (например, Склад), чтобы потом не обращаться к ним через точку.
ВЫБРАТЬ
ЧекККМ.Ссылка КАК Ссылка,
ЧекККМ.Склад КАК Склад
ПОМЕСТИТЬ ВТ_ЧекККМ
ИЗ
Документ.ЧекККМ КАК ЧекККМ
ГДЕ
ЧекККМ.Дата МЕЖДУ &ДатаНачала И &ДатаОкончания
ИНДЕКСИРОВАТЬ ПО
Ссылка
;
Почему это работает? Мы получили компактный список документов. Поле Дата в основной таблице документа проиндексировано, поэтому отбор по периоду отработает мгновенно. Использование ИНДЕКСИРОВАТЬ ПО позволит SQL-серверу быстрее выполнять последующие соединения.
После того как у нас есть список нужных документов, мы можем обращаться к их табличным частям. Вместо прямого соединения ТЧ друг с другом, лучше соединять их последовательно с нашей временной таблицей ВТ_ЧекККМ.
Выясним причину использования поля КлючСвязи. В 1С это поле специально предназначено для связи строк из разных табличных частей одного документа (например, конкретной строки товара с примененными к ней скидками). Соединение только по Ссылка приведет к дублированию строк, так как для каждой строки товара будут выбраны все скидки документа. Поэтому условие по КлючСвязи обязательно.
Разберем пример оптимизированного соединения:
ВЫБРАТЬ
ЧекККМТовары.Номенклатура КАК Номенклатура,
ЧекККМТовары.СуммаАвтоматическойСкидки КАК Скидка,
ЧекККМСкидкиНаценки.СкидкаНаценка КАК СкидкаНаценка,
ЧекККМТовары.Продавец КАК Продавец,
ВТ_ЧекККМ.Склад КАК Склад
ИЗ
ВТ_ЧекККМ КАК ВТ_ЧекККМ
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.ЧекККМ.Товары КАК ЧекККМТовары
ПО ВТ_ЧекККМ.Ссылка = ЧекККМТовары.Ссылка
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Документ.ЧекККМ.СкидкиНаценки КАК ЧекККМСкидкиНаценки
ПО ВТ_ЧекККМ.Ссылка = ЧекККМСкидкиНаценки.Ссылка
И ЧекККМТовары.КлючСвязи = ЧекККМСкидкиНаценки.КлючСвязи
Проанализируем еще один важный момент: выбор между ЛЕВОЕ СОЕДИНЕНИЕ и ВНУТРЕННЕЕ СОЕДИНЕНИЕ. Если мы используем ВНУТРЕННЕЕ СОЕДИНЕНИЕ, то в результат попадут только те товары, по которым есть записи в таблице скидок. Если же нам нужны все проданные товары (даже без скидок), нужно использовать ЛЕВОЕ СОЕДИНЕНИЕ.
Важное замечание: При использовании левого соединения крайне желательно дублировать условия фильтрации в параметрах связи, если это возможно, либо убедиться, что ведущая таблица максимально ограничена по объему данных.
Рассмотрим продвинутый вариант с индексацией КлючСвязи. Хотя в физических таблицах SQL это поле не всегда имеет индекс, во временных таблицах 1С мы можем принудительно проиндексировать данные. Рассмотрим схему:
ВТ_Товары и индексируем по Ссылка и КлючСвязи.ВТ_Скидки и также индексируем.Это может показаться избыточным, но на объемах в десятки тысяч строк такой подход работает значительно стабильнее, так как исключает "сканирование" (Full Scan) больших таблиц.
Сведем все рекомендации в один эффективный запрос, который будет работать быстро даже в крупных базах УТ 11.4:
ВЫБРАТЬ
ЧекККМ.Ссылка КАК Ссылка,
ЧекККМ.Склад КАК Склад
ПОМЕСТИТЬ ВТ_СписокЧеков
ИЗ
Документ.ЧекККМ КАК ЧекККМ
ГДЕ
ЧекККМ.Дата МЕЖДУ &ДатаНачала И &ДатаОкончания
ИНДЕКСИРОВАТЬ ПО
Ссылка
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Товары.Ссылка КАК Ссылка,
Товары.Номенклатура КАК Номенклатура,
Товары.Продавец КАК Продавец,
Товары.СуммаАвтоматическойСкидки КАК Скидка,
Товары.КлючСвязи КАК КлючСвязи
ПОМЕСТИТЬ ВТ_ТоварыЧеков
ИЗ
Документ.ЧекККМ.Товары КАК Товары
ГДЕ
Товары.Ссылка В (ВЫБРАТЬ Ссылка ИЗ ВТ_СписокЧеков)
ИНДЕКСИРОВАТЬ ПО
Ссылка,
КлючСвязи
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Скидки.Ссылка КАК Ссылка,
Скидки.КлючСвязи КАК КлючСвязи,
Скидки.СкидкаНаценка КАК СкидкаНаценка
ПОМЕСТИТЬ ВТ_СкидкиЧеков
ИЗ
Документ.ЧекККМ.СкидкиНаценки КАК Скидки
ГДЕ
Скидки.Ссылка В (ВЫБРАТЬ Ссылка ИЗ ВТ_СписокЧеков)
ИНДЕКСИРОВАТЬ ПО
Ссылка,
КлючСвязи
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ_ТоварыЧеков.Номенклатура КАК Номенклатура,
ВТ_ТоварыЧеков.Скидка КАК Скидка,
ВТ_СкидкиЧеков.СкидкаНаценка КАК СкидкаНаценка,
ВТ_ТоварыЧеков.Продавец КАК Продавец,
ВТ_СписокЧеков.Склад КАК Склад
ИЗ
ВТ_СписокЧеков КАК ВТ_СписокЧеков
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ТоварыЧеков КАК ВТ_ТоварыЧеков
ПО ВТ_СписокЧеков.Ссылка = ВТ_ТоварыЧеков.Ссылка
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_СкидкиЧеков КАК ВТ_СкидкиЧеков
ПО ВТ_ТоварыЧеков.Ссылка = ВТ_СкидкиЧеков.Ссылка
И ВТ_ТоварыЧеков.КлючСвязи = ВТ_СкидкиЧеков.КлючСвязи
Проанализировав ситуацию, мы выяснили, что "простые" запросы в 1С часто скрывают под собой тяжелые операции для СУБД. Основными инструментами оптимизации для нас стали:
Ссылка внутри больших запросов — это основной враг производительности.ИНДЕКСИРОВАТЬ ПО во временных таблицах гарантирует, что SQL-сервер выберет оптимальный способ соединения данных.Применение этих правил позволяет сократить время выполнения запроса с 50-60 секунд до долей секунды, обеспечивая комфортную работу пользователей даже в высоконагруженных розничных системах.