【问题标题】:SQL Server - stored procedure suddenly become slowSQL Server - 存储过程突然变慢
【发布时间】:2011-01-28 13:43:19
【问题描述】:

我昨天写了一个存储过程,通常在一秒钟内完成。今天,大约需要 18 秒。我昨天也遇到了这个问题,似乎可以通过删除并重新创建存储过程来解决。今天,这个技巧似乎不起作用。 :(

有趣的是,如果我复制存储过程的主体并将其作为一个简单的查询执行,它会很快完成。似乎是一个存储过程减慢了它的速度......!

有谁知道可能是什么问题?我已经搜索过答案,但他们通常建议通过 Query Analyser 运行它,但我没有 - 我现在使用的是 SQL Server 2008 Express。

存储过程如下;

更改程序 [dbo].[spGetPOI] @lat1 浮动, @lon1 浮动, @lat2 浮动, @lon2 浮动, @minLOD 小整数, @maxLOD 小整数, @精确位 作为 开始 -- 将查询矩形创建为多边形 声明@bounds 地理; SET @bounds = dbo.fnGetRectangleGeographyFromLatLons(@lat1, @lon1, @lat2, @lon2); -- 执行选择 如果 (@exact = 0) 开始 选择 [ID], [名称], [类型], [数据], [MinLOD], [MaxLOD], [Location].[Lat] AS [Latitude], [Location].[Long] AS [Longitude], [来源ID] 来自 [兴趣点] 在哪里 不是 ((@maxLOD [MaxLOD])) 和 (@bounds.Filter([位置]) = 1) 结尾 别的 开始 选择 [ID], [名称], [类型], [数据], [MinLOD], [MaxLOD], [Location].[Lat] AS [Latitude], [Location].[Long] AS [Longitude], [来源ID] 来自 [兴趣点] 在哪里 不是 ((@maxLOD [MaxLOD])) 和 (@bounds.STIntersects([位置]) = 1) 结尾 结束

“POI”表在 MinLOD、MaxLOD 上有一个索引,在 Location 上有一个空间索引。

【问题讨论】:

    标签: sql stored-procedures indexing spatial


    【解决方案1】:

    啊,会不会是查询计划很烂?

    SP 的编译/查询 lpan 在首次使用时确定 - 取决于参数。因此,第一次调用的参数(当没有 lpan 时)决定了查询计划。在某一时刻,我从缓存中删除,生成了新计划。

    下次运行缓慢时,可能会使用查询分析器进行调用并获取所选计划 - 并检查其外观。

    如果是这样 - 放入一个选项以在每次调用时重新编译 SP(使用重新编译)。

    【讨论】:

    • 这很可能是它。我更改了存储过程以在每次调用时重新编译,并且似乎(现在!)显着加快了速度。谢谢!有没有办法避免重新编译和错误的查询计划?
    • 没有。您可以做的是转储存储过程并使用动态 sql... 这样查询计划就会在每次调用时重新生成。这是存储过程的一个已知问题(似乎并非所有人都理解它,比如 KM)——它们有时会变成非常糟糕的查询计划。
    • 2008 年还有另一个选项:'option (optimize for unknown)'。它避免了“首次使用”问题,并且每次都不需要重新编译。见blogs.msdn.microsoft.com/sqlprogrammability/2008/11/26/…
    【解决方案2】:

    参数嗅探google it。试试这个,它会将输入参数“重新映射”到局部变量,以防止 SQL Server 尝试根据参数猜测查询计划:

    ALTER PROCEDURE [dbo].[spGetPOIs]
        @lat1 float,
        @lon1 float,
        @lat2 float,
        @lon2 float,
        @minLOD tinyint, 
        @maxLOD tinyint,
        @exact bit
    AS
    BEGIN
    DECLARE @X_lat1 float,
        @X_lon1 float,
        @X_lat2 float,
        @X_lon2 float,
        @X_minLOD tinyint, 
        @X_maxLOD tinyint,
        @X_exact bit
    
    
    
        -- Create the query rectangle as a polygon
        DECLARE @bounds geography;
        SET @bounds = dbo.fnGetRectangleGeographyFromLatLons(@X_lat1, @X_lon1, @lX_at2, @X_lon2);
    
        -- Perform the selection
        if (@exact = 0)
        BEGIN
            SELECT [ID], [Name], [Type], [Data], [MinLOD], [MaxLOD], [Location].[Lat] AS [Latitude], [Location].[Long] AS [Longitude], [SourceID]
            FROM [POIs]
            WHERE
                NOT ((@X_maxLOD  [MaxLOD])) AND
                (@bounds.Filter([Location]) = 1)
        END
        ELSE
        BEGIN
            SELECT [ID], [Name], [Type], [Data], [MinLOD], [MaxLOD], [Location].[Lat] AS [Latitude], [Location].[Long] AS [Longitude], [SourceID]
            FROM [POIs]
            WHERE
                NOT ((@X_maxLOD  [MaxLOD])) AND
                (@bounds.STIntersects([Location]) = 1)
        END
    
    END
    

    【讨论】:

    • 我已经尝试过了,但似乎并没有什么不同。我得再试一次。
    【解决方案3】:

    我遇到了类似的问题,它与索引有关。
    重建它们有助于 SP 再次快速运行。

    我找到了解决方案here

    USE master;
    GO
    
    CREATE PROC DatabaseReIndex(@Database VARCHAR(100)) AS 
    BEGIN
      DECLARE @DbID SMALLINT=DB_ID(@Database)--Get Database ID
      IF EXISTS(SELECT * FROM tempdb.sys.objects WHERE name='Indexes') 
      BEGIN --Delete Temp Table if exists, then create
        DROP TABLE TempDb.dbo.Indexes
      END
    
    CREATE TABLE TempDb.dbo.Indexes(IndexTempID INT IDENTITY(1,1),SchemaName NVARCHAR(128),TableName NVARCHAR(128),IndexName NVARCHAR(128),IndexFrag FLOAT)
    EXEC ('USE '+@Database+';
    INSERT INTO TempDb.dbo.Indexes(TableName,SchemaName,IndexName,IndexFrag)
    SELECT OBJECT_NAME(ind.OBJECT_ID) AS TableName,sch.name,ind.name IndexName,indexstats.avg_fragmentation_in_percent
    FROM sys.dm_db_index_physical_stats('+@DbID+', NULL, NULL, NULL, NULL) indexstats
    INNER JOIN sys.indexes ind ON ind.object_id = indexstats.object_id AND ind.index_id = indexstats.index_id
    INNER JOIN sys.objects obj on obj.object_id=indexstats.object_id
    INNER JOIN sys.schemas as sch ON sch.schema_id = obj.schema_id
    WHERE indexstats.avg_fragmentation_in_percent > 10 AND indexstats.index_type_desc<>''HEAP''
    ORDER BY indexstats.avg_fragmentation_in_percent DESC')--Get index data and fragmentation, set the percentage as high or low as you need
    
    DECLARE @IndexTempID BIGINT=0,@SchemaName NVARCHAR(128),@TableName NVARCHAR(128),@IndexName NVARCHAR(128),@IndexFrag FLOAT
    SELECT * FROM TempDb.dbo.Indexes --View your results, comment out if not needed...
    
    -- Loop through the indexes
    WHILE @IndexTempID IS NOT NULL 
      BEGIN
        SELECT @SchemaName=SchemaName,@TableName=TableName,@IndexName=IndexName,@IndexFrag=IndexFrag FROM TempDb.dbo.Indexes WHERE IndexTempID=@IndexTempID
        IF @IndexName IS NOT NULL AND @SchemaName IS NOT NULL AND @TableName IS NOT NULL 
        BEGIN
          IF @IndexFrag<30. 
          BEGIN --Low fragmentation can use re-organise, set at 30 as per most articles
            PRINT 'USE '+@Database+'; ALTER INDEX ' + @IndexName + N' ON ' + @SchemaName + N'.' + @TableName + N' REORGANIZE'
            EXEC('USE '+@Database+'; ALTER INDEX ' + @IndexName + N' ON ' + @SchemaName + N'.' + @TableName + N' REORGANIZE')
          END
        ELSE 
          BEGIN --High fragmentation needs re-build
            PRINT 'USE '+@Database+'; ALTER INDEX ' + @IndexName + N' ON ' + @SchemaName + N'.' + @TableName + N' REBUILD'
            EXEC('USE '+@Database+'; ALTER INDEX ' + @IndexName + N' ON ' + @SchemaName + N'.' + @TableName + N' REBUILD')
          END
        END
    
        SET @IndexTempID=(SELECT MIN(IndexTempID) FROM TempDb.dbo.Indexes WHERE IndexTempID>@IndexTempID)
      END
    END
    
    DROP TABLE TempDb.dbo.Indexes
    
    GO
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-05-10
      • 1970-01-01
      • 2014-07-23
      • 2018-09-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多