SQL Server - 存储过程突然变慢

发布于 2024-08-25 02:40:40 字数 1288 浏览 7 评论 0原文

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

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

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

如下;

ALTER PROCEDURE [dbo].[spGetPOIs]
    @lat1 float,
    @lon1 float,
    @lat2 float,
    @lon2 float,
    @minLOD tinyint, 
    @maxLOD tinyint,
    @exact bit
AS
BEGIN
    -- Create the query rectangle as a polygon
    DECLARE @bounds geography;
    SET @bounds = dbo.fnGetRectangleGeographyFromLatLons(@lat1, @lon1, @lat2, @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 ((@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 ((@maxLOD  [MaxLOD])) AND
            (@bounds.STIntersects([Location]) = 1)
    END

END

存储过程 表具有 MinLOD、MaxLOD 索引和 Location 空间索引。

I have written a stored procedure that, yesterday, typically completed in under a second. Today, it takes about 18 seconds. I ran into the problem yesterday as well, and it seemed to be solved by DROPing and re-CREATEing the stored procedure. Today, that trick doesn't appear to be working. :(

Interestingly, if I copy the body of the stored procedure and execute it as a straightforward query it completes quickly. It seems to be the fact that it's a stored procedure that's slowing it down...!

Does anyone know what the problem might be? I've searched for answers, but often they recommend running it through Query Analyser, but I don't have have it - I'm using SQL Server 2008 Express for now.

The stored procedure is as follows;

ALTER PROCEDURE [dbo].[spGetPOIs]
    @lat1 float,
    @lon1 float,
    @lat2 float,
    @lon2 float,
    @minLOD tinyint, 
    @maxLOD tinyint,
    @exact bit
AS
BEGIN
    -- Create the query rectangle as a polygon
    DECLARE @bounds geography;
    SET @bounds = dbo.fnGetRectangleGeographyFromLatLons(@lat1, @lon1, @lat2, @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 ((@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 ((@maxLOD  [MaxLOD])) AND
            (@bounds.STIntersects([Location]) = 1)
    END

END

The 'POI' table has an index on MinLOD, MaxLOD, and a spatial index on Location.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

伤感在游骋 2024-09-01 02:40:40

啊,难道查询计划很糟糕吗?

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

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

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

Ah, can it be the query plan sucks?

SP's get compiled / query plan determined on FIRST USE - depending on parameters. So, the parameters of the first call (when no plan is present) determine the query plan. At one point I gets dropped from cache, new plan generated.

Next time it runs slow, possibly make a call using query analyzer and get the selected plan - and check how it looks.

if it is this - put in an option to recompile the SP on every call (with recompile).

可遇━不可求 2024-09-01 02:40:40

parameter sniffing 谷歌一下。 try this, which will "remap" the input parameters to local variables to prevent SQL Server from trying to guess the query plan based on parameters:

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

parameter sniffing google it. try this, which will "remap" the input parameters to local variables to prevent SQL Server from trying to guess the query plan based on parameters:

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
桜花祭 2024-09-01 02:40:40

我有一个类似的问题,它与索引有关。
重建它们可以帮助 SP 再次快速运行。

我在此处找到了解决方案

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

I had a similar problem and it was related with indexes.
Rebuilding them help the SP to run fast again.

I found the solution 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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文