Почему хранимая процедура запускается медленнее, чем голый T-SQL? - PullRequest
4 голосов
/ 27 августа 2010

У меня есть хранимая процедура в базе данных MS-SQL 2005, которая:

  • Создает две временные таблицы
  • Выполняет запрос с 7 объединениями, но в остальном не очень сложен
  • Вставляет результаты в одну из временных таблиц
  • Выполняет еще два запроса (без присоединения к «реальным» таблицам), которые помещают записи из одной из временных таблиц в другую.
  • Возвращает набор результатов из второй временной таблицы
  • Удаляет обе временные таблицы

SP принимает два параметра, которые затем используются в первом запросе.

Когда я запускаю SP для заданного набора параметров, выполнение занимает 3 минуты.

Когда я выполняю содержимое SP в виде обычного пакета T-SQL (предварительно объявив и установив параметры), это займет 10 секунд. Эти числа одинаковы для нескольких последовательных прогонов.

Это огромная разница, и нет никаких очевидных функциональных изменений. Что может быть причиной этого?

UPDATE

Повторная индексация моих таблиц (DBCC REINDEX) значительно ускорила версию SP . Версия SP теперь занимает 1 секунду, а сырой SQL - 6.

Это замечательно в качестве решения насущной проблемы, но я все же хотел бы знать «почему».

Ответы [ 4 ]

11 голосов
/ 27 августа 2010

Это могло быть именно из-за того, что в SP план выполнения кэшировался и не был оптимальным для набора данных. Когда набор данных сильно зависит от параметров или значительно изменяется между вызовами, лучше указывать «с перекомпиляцией» в «create proc». Вы теряете долю секунды при перекомпиляции, но можете выиграть минуты при выполнении.

PS Почему я не могу комментировать? Доступен только «Ваш ответ».

2 голосов
/ 07 апреля 2011

Недавно я сталкивался с точно такой же проблемой пару раз (с MS-SQL 2008).Определенные хранимые процедуры будут работать очень медленно (минуты), но тот же SQL, вставленный в SSMS, занял всего несколько секунд.

Проблема в основном в том, что хранимая процедура использует неверный план выполнения, в то время как вставленный SQL используетдругой (и намного лучший) план выполнения.

Сравнить планы выполнения

Чтобы проверить эту гипотезу, откройте новое окно запроса в SSMS и включите «Включить фактический план выполнения» (Ctrl-M - этосочетание клавиш для этого).

Затем вставьте содержимое хранимой процедуры в окно и выполните это с вызовом фактической хранимой процедуры.Например:

SELECT FirstName, LastName FROM Пользователи, где ID = 10

EXEC dbo.spGetUserById 10

Выполните оба запроса вместе, а затем сравнитепланы выполнения для обоих.Я должен сказать, что в моем случае оценка «стоимости запроса» для каждого запроса вообще не помогла и указала мне неправильное направление.Вместо этого внимательно посмотрите на используемые индексы, выполняются ли операции сканирования вместо поиска, и количество обрабатываемых строк.

Должна быть разница в планах, и это должно помочь идентифицировать таблицы и индексы, которые необходимо изучить далее.

Чтобы помочь решить проблему, в одном случае мне удалось переписатьхранимая процедура, чтобы избежать использования сканирования индекса и вместо этого полагаться на поиск индекса.В другом случае я обнаружил, что обновление этой перестройки индексов для конкретной таблицы, используемой в запросе, имело все значение.

Поиск и обновление индексов

Я использовал этот SQL для поиска иперестройте соответствующие индексы:

/* Originally created by Microsoft */
/* Error corrected by Pinal Dave (http://www.SQLAuthority.com) */
/* http://blog.sqlauthority.com/2008/03/04/sql-server-2005-a-simple-way-to-defragment-all-indexes-in-a-database-that-is-fragmented-above-a-declared-threshold/ */
/* Catch22: Added parameters to filter by table & view proposed changes */

-- Specify your Database Name
USE AdventureWorks

/* Parameters */
Declare @MatchingTableName nvarchar(100) = 'MyTablePrefix'  -- Specify Table name (can be prefix of table name) or blank for all tables
DECLARE @ViewOnly bit = 1                                   -- Set to 1 to view proposed actions, set to 0 to Execute proposed actions:


-- Declare variables
SET NOCOUNT ON
DECLARE @tablename VARCHAR(128)
DECLARE @execstr VARCHAR(255)
DECLARE @objectid INT
DECLARE @indexid INT
DECLARE @frag decimal
DECLARE @maxreorg decimal
DECLARE @maxrebuild decimal
DECLARE @IdxName varchar(128)
DECLARE @ReorgOptions varchar(255)
DECLARE @RebuildOptions varchar(255)

-- Decide on the maximum fragmentation to allow for a reorganize.
-- AVAILABLE OPTIONS: http://technet.microsoft.com/en-us/library/ms188388(SQL.90).aspx
SET @maxreorg = 20.0
SET @ReorgOptions = 'LOB_COMPACTION=ON'
-- Decide on the maximum fragmentation to allow for a rebuild.
SET @maxrebuild = 30.0
-- NOTE: only specifiy FILLFACTOR=x if x is something other than zero:
SET @RebuildOptions = 'PAD_INDEX=OFF, SORT_IN_TEMPDB=ON, STATISTICS_NORECOMPUTE=OFF, ALLOW_ROW_LOCKS=ON, ALLOW_PAGE_LOCKS=ON'

-- Declare a cursor.
DECLARE tables CURSOR FOR
SELECT CAST(TABLE_SCHEMA AS VARCHAR(100))
+'.'+CAST(TABLE_NAME AS VARCHAR(100))
AS Table_Name
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE = 'BASE TABLE'
AND TABLE_NAME like @MatchingTableName + '%'

-- Create the temporary table.
if exists (select name from tempdb.dbo.sysobjects where name like '#fraglist%')
drop table #fraglist

CREATE TABLE #fraglist (
ObjectName CHAR(255),
ObjectId INT,
IndexName CHAR(255),
IndexId INT,
Lvl INT,
CountPages INT,
CountRows INT,
MinRecSize INT,
MaxRecSize INT,
AvgRecSize INT,
ForRecCount INT,
Extents INT,
ExtentSwitches INT,
AvgFreeBytes INT,
AvgPageDensity INT,
ScanDensity decimal,
BestCount INT,
ActualCount INT,
LogicalFrag decimal,
ExtentFrag decimal)

-- Open the cursor.
OPEN tables

-- Loop through all the tables in the database.
FETCH NEXT
FROM tables
INTO @tablename

WHILE @@FETCH_STATUS = 0
BEGIN
-- Do the showcontig of all indexes of the table
INSERT INTO #fraglist
EXEC ('DBCC SHOWCONTIG (''' + @tablename + ''')
WITH FAST, TABLERESULTS, ALL_INDEXES, NO_INFOMSGS')
FETCH NEXT
FROM tables
INTO @tablename
END

-- Close and deallocate the cursor.
CLOSE tables
DEALLOCATE tables

-- Declare the cursor for the list of indexes to be defragged.
DECLARE indexes CURSOR FOR
SELECT ObjectName, ObjectId, IndexId, LogicalFrag, IndexName
FROM #fraglist
WHERE ((LogicalFrag >= @maxreorg) OR (LogicalFrag >= @maxrebuild))
AND INDEXPROPERTY (ObjectId, IndexName, 'IndexDepth') > 0

-- Open the cursor.
OPEN indexes

-- Loop through the indexes.
FETCH NEXT
FROM indexes
INTO @tablename, @objectid, @indexid, @frag, @IdxName

WHILE @@FETCH_STATUS = 0
BEGIN
IF (@frag >= @maxrebuild)
BEGIN
IF (@ViewOnly=1)
BEGIN
PRINT 'WOULD be executing ALTER INDEX ' + RTRIM(@IdxName) + ' ON ' + RTRIM(@tablename) + ' REBUILD WITH ( ' + @RebuildOptions + ' ) -- Fragmentation currently ' + RTRIM(CONVERT(VARCHAR(15),@frag)) + '%'
END
ELSE
BEGIN
PRINT 'Now executing ALTER INDEX ' + RTRIM(@IdxName) + ' ON ' + RTRIM(@tablename) + ' REBUILD WITH ( ' + @RebuildOptions + ' ) -- Fragmentation currently ' + RTRIM(CONVERT(VARCHAR(15),@frag)) + '%'
SELECT @execstr = 'ALTER INDEX ' + RTRIM(@IdxName) + ' ON ' + RTRIM(@tablename) + ' REBUILD WITH ( ' + @RebuildOptions + ' )'
EXEC (@execstr)
END
END
ELSE IF (@frag >= @maxreorg)
BEGIN
IF (@ViewOnly=1)
BEGIN
PRINT 'WOULD be executing ALTER INDEX ' + RTRIM(@IdxName) + ' ON ' + RTRIM(@tablename) + ' REORGANIZE WITH ( ' + @ReorgOptions + ' ) -- Fragmentation currently ' + RTRIM(CONVERT(VARCHAR(15),@frag)) + '%'
END
ELSE
BEGIN
PRINT 'Now executing ALTER INDEX ' + RTRIM(@IdxName) + ' ON ' + RTRIM(@tablename) + ' REORGANIZE WITH ( ' + @ReorgOptions + ' ) -- Fragmentation currently ' + RTRIM(CONVERT(VARCHAR(15),@frag)) + '%'
SELECT @execstr = 'ALTER INDEX ' + RTRIM(@IdxName) + ' ON ' + RTRIM(@tablename) + ' REORGANIZE WITH ( ' + @ReorgOptions + ' )'
EXEC (@execstr)
END
END

FETCH NEXT
FROM indexes
INTO @tablename, @objectid, @indexid, @frag, @IdxName
END

-- Close and deallocate the cursor.
CLOSE indexes
DEALLOCATE indexes

-- Delete the temporary table.
DROP TABLE #fraglist
0 голосов
/ 16 октября 2013

эта проблема решена с помощью различных подходов, как показал Грег Ларсен Посетите https://www.simple -talk.com / sql / t-sql-программирование / анализ параметров /

0 голосов
/ 27 августа 2010

Ваш SP вообще использует динамический T-SQL? Если это так, вы потеряете преимущества кэшированных планов выполнения ...

В противном случае соединения, используемые для запуска SP и T-SQL, настроены таким же образом? Является ли разница в скорости постоянной или SP настолько медленный в первый раз, когда работает после движения?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...