代码之家  ›  专栏  ›  技术社区  ›  Craig Walker

为什么存储过程的运行速度比裸T-SQL慢?

  •  4
  • Craig Walker  · 技术社区  · 15 年前

    我在MS-SQL 2005数据库中有一个存储过程:

    • 创建两个临时表
    • 执行包含7个连接的查询,但在其他方面并不十分复杂
    • 将结果插入其中一个临时表
    • 再执行两个查询(不连接到“实”表),将记录从一个临时表放入另一个临时表。
    • 返回第二个临时表的结果集

    SP接受两个参数,然后在第一个查询中使用。

    当我为一组给定的参数运行SP时,需要3分钟才能执行。

    当我执行 目录 将SP作为常规的T-SQL批处理(预先声明和设置参数),需要10秒钟。这些数字在多个连续运行中是一致的。

    这是一个巨大的差异,没有明显的功能变化。是什么原因造成的?

    更新

    重新索引我的表(DBCC REINDEX)加快了SP版本的速度 戏剧性地 . SP版本现在需要1秒,而原始SQL需要6秒。

    这是解决眼前问题的好办法,但我还是想知道“为什么”。

    4 回复  |  直到 15 年前
        1
  •  11
  •   vaso    15 年前

    这可能正是由于在SP中,执行计划被缓存了,对于数据集来说不是最佳的。当数据集很大程度上依赖于参数或在调用之间发生很大变化时,最好在“create proc”中指定“with recompile”。您在重新编译时损失了几分之一秒,但在执行时可能赢得几分钟。

        2
  •  2
  •   Catch22    15 年前

    我最近也遇到过几次完全相同的问题(使用MS-SQL2008)。特定的存储过程运行起来会非常慢(几分钟),但粘贴到SSMS中的同一个SQL只需几秒钟。

    比较执行计划

    Ctrl-M 是此的键盘快捷键)。

    然后将存储过程的内容粘贴到窗口中,然后调用实际的存储过程。

    从ID=10的用户中选择FirstName和LastName

    同时运行两个查询,然后比较两个查询的执行计划。我不得不说,在我的例子中,对每个查询的“查询成本”估计根本没有帮助,并且把我指向了错误的方向。相反,请仔细查看正在使用的索引、是否正在执行扫描而不是查找以及正在处理的行数。

    计划应该有所不同,这有助于确定需要进一步调查的表和索引。

    在另一个实例中,我发现更新并重建查询中使用的特定表的索引会产生很大的不同。

    我已使用此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
    
        3
  •  0
  •   Basic    15 年前

    您的SP是否使用动态T-SQL?如果是这样,你就失去了缓存执行计划的好处。。。

    否则,用于运行SP vs T-SQL的连接是否以相同的方式配置?速度差是一致的还是湿化后SP第一次运行时一样慢?

    推荐文章