При работе с прямыми SQL-запросами к базе данных 1С:Предприятия 7.7 на MS SQL Server разработчики нередко сталкиваются с аномальным поведением: запрос, возвращающий небольшой массив данных за доли секунды, после добавления инструкции TOP 1 начинает выполняться 10–15 секунд и более. Проанализируем, почему возникает эта проблема, как работает оптимизатор СУБД и какими способами можно вернуть высокую скорость выполнения запроса.
Рассмотрим пример запроса, выбирающего подчиненные документы и соединяющего их с журналом документов _1sjourn:
SELECT --TOP 1
ЖурналПодч2.*
FROM
(SELECT
childid
FROM
_1scrdoc as Подч (nolock)
WHERE
(mdid = 0) AND (ParentVal = 'O1 BE BYLOKRAD ')) ПодчДок
INNER JOIN
_1sjourn as ЖурналПодч (Nolock) ON ЖурналПодч.iddocdef = $ВидДокумента.Рейс AND ЖурналПодч.iddoc = ПодчДок.childid
INNER JOIN
_1scrdoc as ПодчДок2 (Nolock) ON ПодчДок2.mdid = 0 AND ПодчДок2.ParentVal = 'o1'+$ВидДокумента36.Рейс + ЖурналПодч.iddoc
INNER JOIN
_1sjourn as ЖурналПодч2 (Nolock) ON ЖурналПодч2.iddocdef = $ВидДокумента.ТранспортнаяНакладная AND ЖурналПодч2.iddoc = ПодчДок2.childid
ORDER BY
ЖурналПодч2.date_time_iddoc desc
Без указания TOP 1 сервер фильтрует записи по индексам, получает небольшое итоговое количество строк (например, 13 записей), сортирует их и моментально выдает результат. Однако при раскомментировании TOP 1 оптимизатор запросов меняет план выполнения из-за механизмов целевого количества строк (Row Goal):
_1sjourn в порядке сортировки date_time_iddoc desc, ожидая быстро встретить первую подходящую запись.Главной причиной неверной оценки стоимости плана запроса оптимизатором являются неактуальные или поврежденные статистики таблиц. Разберем порядок их обновления.
Часто используемая процедура sp_updatestats выполняет сэмплированное (выборочное) обновление. Для таблиц связей документов 1С (таких как _1scrdoc и _1sjourn) этого бывает недостаточно. Выполним полное обновление статистик с помощью параметра FULLSCAN:
UPDATE STATISTICS _1scrdoc WITH FULLSCAN;
UPDATE STATISTICS _1sjourn WITH FULLSCAN;
Также можно запустить полное обновление статистик всей базы данных через планы обслуживания (Maintenance Plans) MS SQL Server. После процедуры сканирования распределения данных оптимизатор снова сможет правильно оценивать селективность условий и выстраивать быстрый план выполнения.
Если изменить настройки сервера или оперативно обновить статистики невозможно, изменим структуру самого T-SQL запроса. Использование агрегатной функции MAX() вместо TOP 1 принудительно заставляет СУБД сначала отфильтровать все подходящие записи и только затем найти максимальное значение, исключая преждевременный переход на сканирование огромных индексов.
Посмотрим на пример переписанного запроса:
SELECT
RIGHT(MAX(ЖурналПодч2.date_time_iddoc), 9) [ТранспортнаяНакладная $Документ.ТранспортнаяНакладная]
FROM
(SELECT
childid
FROM
_1scrdoc as Подч (nolock)
WHERE
(mdid = 0) AND (ParentVal = 'O1 BE BYLOKRAD ')) ПодчДок
INNER JOIN
_1sjourn as ЖурналПодч (Nolock) ON ЖурналПодч.iddocdef = $ВидДокумента.Рейс AND ЖурналПодч.iddoc = ПодчДок.childid
INNER JOIN
_1scrdoc as ПодчДок2 (Nolock) ON ПодчДок2.mdid = 0 AND ПодчДок2.ParentVal = 'o1'+$ВидДокумента36.Рейс + ЖурналПодч.iddoc
INNER JOIN
_1sjourn as ЖурналПодч2 (Nolock) ON ЖурналПодч2.iddocdef = $ВидДокумента.ТранспортнаяНакладная AND ЖурналПодч2.iddoc = ПодчДок2.childid
Этот вариант отрабатывает за доли секунды, так как СУБД не пытается применить алгоритм Row Goal для ранней остановки чтения строк.
Выясним еще один надежный способ изоляции логики выбора — использование временных таблиц. Сохранив отфильтрованные данные во временную таблицу #temp, мы разрываем единый контекст оптимизации сложности запроса:
TOP 1 и сохраняем результаты во временную таблицу #temp.SELECT TOP 1 ... FROM #temp ORDER BY .... Поскольку временная таблица содержит всего несколько строк, операция ограничения выборки выполняется мгновенно.Для предотвращения подобных проблем в высоконагруженных системах 1С настроим регулярные регламентные процедуры:
1. Проводим дефрагментацию и перестроение индексов (REORGANIZE / REBUILD) в часы наименьшей нагрузки на сервер.
2. Обновляем частотные статистики таблиц 1С с параметром FULLSCAN не реже одного раза в неделю, а для активно меняющихся таблиц документов — ежедневно.
3. В современных версиях СУБД (MS SQL Server 2008 и новее) механизмы оценки кардинальности работают значительно точнее, чем в MS SQL Server 2000, поэтому обновление версии сервера также решает большинство проблем с неоптимальными планами выполнения TOP N.