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