Как ускорить выборку данных из таблиц с десятками миллионов строк в 1С?

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

При работе с крупными базами данных, где размер таблиц исчисляется десятками миллионов записей, стандартные подходы к написанию запросов могут приводить к значительным задержкам или полной остановке системы. Особенно остро проблема проявляется при миграции на новый сервер, когда статистика СУБД еще не обновлена, а ресурсы распределяются иначе. В данной статье мы разберем, как оптимизировать тяжелый запрос, использующий ЛЕВОЕ СОЕДИНЕНИЕ и фильтрацию по отсутствующим записям (Anti-Join).

Анализ исходной проблемы

Рассмотрим ситуацию: у нас есть огромная таблица документов Документ.Транзакция и регистр сведений РегистрСведений.Нумератор. Нам необходимо выбрать первые 1000 транзакций, которых еще нет в регистре. Первоначальный алгоритм автора предполагал помещение данных во временную таблицу (ВТ), а затем выборку из нее с условием ЕСТЬ NULL и сортировкой. На больших объемах этот подход "глохнет", так как создание ВТ без индексов и последующее разыменование полей при сортировке создают колоссальную нагрузку на дисковую подсистему и процессор сервера.

Отказ от временных таблиц при использовании оператора ПЕРВЫЕ

Одной из главных ошибок в запросах к большим данным является неоправданное использование временных таблиц для промежуточного хранения. Если нам нужно получить ограниченный набор данных (например, ПЕРВЫЕ 1000), помещение всего массива во временную таблицу заставляет СУБД сначала сформировать полный результат в tempdb, проиндексировать его (если указано) и только потом отсечь лишнее.

Разберем, почему лучше объединить запрос. Когда мы используем единую конструкцию ВЫБРАТЬ ПЕРВЫЕ, оптимизатор SQL может применить стратегию Top Scan. Он прекратит выполнение запроса сразу, как только найдет первую тысячу подходящих записей, не сканируя всю таблицу целиком. Посмотрим на оптимизированный вариант кода, который "взлетел" у автора:


ВЫБРАТЬ ПЕРВЫЕ 1000  
    Таблица2.transaction_id, 
    Таблица2.amount,
    Таблица2.Номер, 
    Таблица1.Номер КАК Номер2 
ИЗ 
    Документ.Транзакция КАК Таблица2 
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.Нумератор КАК Таблица1 
    ПО Таблица2.Ссылка = Таблица1.Документ
ГДЕ 
    Таблица2.Проведен 
    И Таблица2.transaction_id <> "" 
    И Таблица1.Документ ЕСТЬ NULL
УПОРЯДОЧИТЬ ПО 
   Таблица2.Номер

Важный нюанс: Обратите внимание, что в итоговом коде условие Таблица1.Номер ЕСТЬ NULL (или проверка по ключевому полю соединения Таблица1.Документ ЕСТЬ NULL) должно находиться в секции ГДЕ, чтобы корректно отработать логику исключения уже существующих записей.

Оптимизация индексов в регистрах сведений

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

  1. Порядок измерений: Поле, по которому идет соединение (Документ), должно стоять на первом месте в списке измерений регистра, либо иметь признак Индексировать.
  2. Составные типы данных: Если измерение Документ имеет составной тип (ссылается на разные виды документов), это существенно усложняет жизнь СУБД. На уровне SQL создается несколько колонок для хранения типа и ссылки. Для ускорения желательно в условии соединения или в фильтрах явно указывать тип через ССЫЛКА.
  3. Индексация ресурсов: Если в запросе используется фильтрация по ресурсам регистра, их необходимо сделать индексируемыми или перенести в измерения.

Проблема разыменования через точку

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

Проанализируем ситуацию: чтобы отсортировать данные по номеру, СУБД вынуждена "сходить" в таблицу документа по каждой ссылке. Чтобы этого избежать, всегда выбирайте нужные примитивные поля (строки, числа, даты) в основном запросе и сортируйте уже по ним, как это сделано в исправленном коде: УПОРЯДОЧИТЬ ПО Таблица2.Номер. Здесь Номер — это уже реквизит самой таблицы транзакции, и дополнительного соединения не требуется.

Оптимизация работы после смены сервера

Часто программисты сталкиваются с тем, что запрос, работавший быстро на старом сервере, "умирает" на новом. Выясним причину этого явления. Основная причина — неактуальная статистика. Оптимизатор СУБД опирается на данные о распределении значений в таблицах. После переноса базы или массовой загрузки данных статистика может быть пустой, и сервер выберет Table Scan (полный обход таблицы) вместо Index Seek (поиск по индексу).

Для решения этой проблемы выполните следующие действия:

Индексирование временных таблиц

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


ВЫБРАТЬ
    Таблица2.Ссылка КАК СсылкаДок
ПОМЕСТИТЬ ВТ_Транзакции
ИЗ
    Документ.Транзакция КАК Таблица2
ГДЕ
    Таблица2.Проведен
ИНДЕКСИРОВАТЬ ПО
    СсылкаДок
;

Без инструкции ИНДЕКСИРОВАТЬ ПО последующее соединение этой ВТ с физической таблицей в 50 миллионов строк превратится в катастрофу, так как сервер будет выполнять вложенные циклы (Nested Loops) без возможности быстрого поиска соответствий.

Итоги

Для успешной работы с огромными объемами данных в 1С придерживайтесь следующих правил:

  1. Старайтесь избегать временных таблиц, если результат нужен один раз и его можно получить прямым запросом.
  2. Используйте ПЕРВЫЕ только вместе с УПОРЯДОЧИТЬ ПО по индексированному полю.
  3. Минимизируйте количество точек в запросе (разыменование ссылок).
  4. Следите за тем, чтобы поля соединения всегда были проиндексированы.
  5. При использовании ЛЕВОЕ СОЕДИНЕНИЕ для поиска отсутствующих данных, убедитесь, что условие ЕСТЬ NULL проверяет ключевое поле (измерение регистра или ссылку).

Эти шаги позволят вашим запросам работать стабильно независимо от объема накопленных данных и мощности серверного оборудования.

← На главную