存储过程非常慢

时间:2011-06-30 23:58:28

标签: sql sql-server stored-procedures sql-server-2008-r2 sql-optimization

我在查询优化方面遇到了困难,目前我非常接近数据库重新设计的重点。 stackoverflow是我最后的希望。我不认为只是向您显示查询就足够了所以我不仅链接数据库脚本而且还附加了数据库备份,以防您不想手动生成数据

Here您可以找到脚本和备份

当您尝试执行以下操作时,问题就开始了......

exec LockBranches @count=64,@lockedBy='034C0396-5C34-4DDA-8AD5-7E43B373AE5A',@lockedOn='2011-07-01 01:29:43.863',@unlockOn='2011-07-01 01:32:43.863'

这个部分出现了主要问题:

UPDATE B
SET B.LockedBy = @lockedBy,
    B.LockedOn = @lockedOn,
    B.UnlockOn = @unlockOn,
    B.Complete = 1
FROM
(
    SELECT TOP (@count) B.LockedBy, B.LockedOn, B.UnlockOn, B.Complete
    FROM Objectives AS O
    INNER JOIN Generations AS G ON G.ObjectiveID = O.ID
    INNER JOIN Branches AS B ON B.GenerationID = G.ID
    INNER JOIN
    (
        SELECT SB.BranchID AS BranchID, SUM(X.SuitableProbes) AS SuitableProbes
        FROM SpicieBranches AS SB
        INNER JOIN Probes AS P ON P.SpicieID = SB.SpicieID
        INNER JOIN
        (
            SELECT P.ID, 1 AS SuitableProbes
            FROM Probes AS P
/* ----> */ INNER JOIN Results AS R ON P.ID = R.ProbeID /* SSMS Estimated execution plan says this operation is the roughest */
            GROUP BY P.ID
            HAVING COUNT(R.ID) > 0
        ) AS X ON P.ID = X.ID
        GROUP BY SB.BranchID
    ) AS X ON X.BranchID = B.ID
    WHERE
            (O.Active = 1)
        AND (B.Sealed = 0)
        AND (B.GenerationNo < O.BranchGenerations)
        AND (B.LockedBy IS NULL OR DATEDIFF(SECOND, B.UnlockOn, GETDATE()) > 0)
        AND (B.Complete = 1 OR X.SuitableProbes = O.BranchSize * O.EstimateCount * O.ProbeCount)        
) AS B

编辑:以下是每个表格中的行数:

Spicies         71536
Results         10240
Probes          10240
SpicieBranches  4096
Branches        256
Estimates       5
Generations     1
Versions        1
Objectives      1

5 个答案:

答案 0 :(得分:2)

这基本上是一个完整的猜测,但在过去的时间里,我发现加入子查询的结果可能会非常慢。也就是说,当子查询真的不需要时,它被评估的次数太多了 解决这个问题的方法是将子查询移动到CTE中,然后将它们连接到CTE上。祝你好运!

答案 1 :(得分:2)

其他人可能能够比我更好地解释为什么这会更快。经验告诉我,当你有一堆共同运行速度缓慢的查询时,应该快速查看各自的部分,然后值得尝试临时表。

这要快得多

ALTER PROCEDURE LockBranches
-- Add the parameters for the stored procedure here  
@count INT,   
@lockedOn DATETIME,  
@unlockOn DATETIME,  
@lockedBy UNIQUEIDENTIFIER 

AS  
BEGIN  
 -- SET NOCOUNT ON added to prevent extra result sets from  
 -- interfering with SELECT statements.  
 SET NOCOUNT ON  

--Create Temp Table
SELECT SpicieBranches.BranchID AS BranchID, SUM(X.SuitableProbes) AS SuitableProbes 
INTO #BranchSuitableProbeCount
FROM SpicieBranches 
INNER JOIN Probes AS P ON P.SpicieID = SpicieBranches.SpicieID  
INNER JOIN  
(  
     SELECT P.ID, 1 AS SuitableProbes  
     FROM Probes AS P  
     INNER JOIN Results AS R ON P.ID = R.ProbeID  
     GROUP BY P.ID  
     HAVING COUNT(R.ID) > 0  
) AS X ON P.ID = X.ID  
GROUP BY SpicieBranches.BranchID


UPDATE B SET 
B.LockedBy = @lockedBy,    
B.LockedOn = @lockedOn,    
B.UnlockOn = @unlockOn,    
B.Complete = 1
FROM
(
  SELECT TOP (@count) Branches.LockedBy, Branches.LockedOn, Branches.UnlockOn, Branches.Complete  
  FROM Objectives  
  INNER JOIN Generations ON Generations.ObjectiveID = Objectives.ID  
  INNER JOIN Branches ON Branches.GenerationID = Generations.ID  
  INNER JOIN #BranchSuitableProbeCount ON Branches.ID = #BranchSuitableProbeCount.BranchID  
  WHERE  
    (Objectives.Active = 1)  
   AND (Branches.Sealed = 0)  
   AND (Branches.GenerationNo < Objectives.BranchGenerations)  
   AND (Branches.LockedBy IS NULL OR DATEDIFF(SECOND, Branches.UnlockOn, GETDATE()) > 0)  
   AND (Branches.Complete = 1 OR #BranchSuitableProbeCount.SuitableProbes = Objectives.BranchSize * Objectives.EstimateCount * Objectives.ProbeCount)
) AS B

END

这比平均执行时间54ms快得多,而原始执行时间为6秒。

修改

看看并将我的想法与RBarryYoung的解决方案结合起来。如果使用以下内容创建临时表

SELECT SB.BranchID AS BranchID, COUNT(*) AS SuitableProbes
INTO #BranchSuitableProbeCount  
FROM SpicieBranches AS SB
INNER JOIN Probes AS P ON P.SpicieID = SB.SpicieID
WHERE EXISTS(SELECT * FROM Results AS R WHERE R.ProbeID = P.ID)
GROUP BY SB.BranchID

然后你可以把它降到15毫秒,比我们开始时好400倍。查看执行计划显示临时表上发生了表扫描。通常情况下,你可以尽可能地避免表扫描,但是对于128行(在这种情况下),它比以前做的更快。

答案 2 :(得分:1)

两个uniqueidentifier列上的联接似乎是问题的根源。一个是聚簇索引,另一个是非聚集在(FK表)上。很好,他们有索引。不幸的是,当加入大量行时,guid的表现非常糟糕。

作为故障排除步骤:

  • 索引处于什么状态?最后一次更新统计信息的时间是什么时候?
  • 当执行adhoc时,子查询对自身有多高效?即,当您自己运行此语句时,结果集返回的速度有多快?上可接受的?
  • 重建2个索引并更新统计数据之后,有没有可衡量的差异?
SELECT P.ID, 1 AS SuitableProbes FROM Probes AS P
INNER JOIN Results AS R ON P.ID = R.ProbeID
GROUP BY P.ID  HAVING COUNT(R.ID) > 0

答案 3 :(得分:1)

以下版本在我的系统上运行速度提高了15倍:

UPDATE B
SET B.LockedBy = @lockedBy,
    B.LockedOn = @lockedOn,
    B.UnlockOn = @unlockOn,
    B.Complete = 1
FROM
(
    SELECT TOP (@count) B.LockedBy, B.LockedOn, B.UnlockOn, B.Complete
    FROM Objectives AS O
    INNER JOIN Generations AS G ON G.ObjectiveID = O.ID
    INNER JOIN Branches AS B ON B.GenerationID = G.ID
    INNER JOIN 
    (
        SELECT SB.BranchID AS BranchID, COUNT(*) AS SuitableProbes
        FROM SpicieBranches AS SB
        INNER JOIN Probes AS P ON P.SpicieID = SB.SpicieID
        WHERE EXISTS(SELECT * FROM Results AS R WHERE R.ProbeID = P.ID)
        GROUP BY SB.BranchID
    ) AS X ON X.BranchID = B.ID
    WHERE
            (O.Active = 1)
        AND (B.Sealed = 0)
        AND (B.GenerationNo < O.BranchGenerations)
        AND (B.LockedBy IS NULL OR DATEDIFF(SECOND, B.UnlockOn, GETDATE()) > 0)
        AND (B.Complete = 1 OR X.SuitableProbes = O.BranchSize * O.EstimateCount * O.ProbeCount)        
) AS B

答案 4 :(得分:1)

将子查询插入本地临时表

SELECT SB.BranchID AS BranchID, SUM(X.SuitableProbes) AS SuitableProbes
into #temp FROM SpicieBranches AS SB
INNER JOIN Probes AS P ON P.SpicieID = SB.SpicieID
INNER JOIN
(
    SELECT P.ID, 1 AS SuitableProbes
    FROM Probes AS P
/* ----> */ INNER JOIN Results AS R ON P.ID = R.ProbeID /* SSMS Estimated execution plan says this operation is the roughest */
    GROUP BY P.ID
    HAVING COUNT(R.ID) > 0
) AS X ON P.ID = X.ID
GROUP BY SB.BranchID

以下查询显示了与相应表的部分连接,而不是完整!!

UPDATE B
SET B.LockedBy = @lockedBy,
    B.LockedOn = @lockedOn,
    B.UnlockOn = @unlockOn,
    B.Complete = 1
FROM
(
    SELECT TOP (@count) B.LockedBy, B.LockedOn, B.UnlockOn, B.Complete
    From
    (
        SELECT ID, BranchGenerations, (BranchSize * EstimateCount * ProbeCount) as MultipliedFactor
        FROM Objectives AS O WHERE (O.Active = 1)
    )O
    INNER JOIN Generations AS G ON G.ObjectiveID = O.ID
    Inner Join
    (
        Select Sealed, GenerationNo, LockedBy, UnlockOn, ID, Complete
        From Branches 
        Where B.Sealed = 0 AND (B.LockedBy IS NULL OR DATEDIFF(SECOND, B.UnlockOn, GETDATE()) > 0)
    )B ON B.GenerationID = G.ID
    INNER JOIN
    (
        Select * from #temp
    ) AS X ON X.BranchID = B.ID
    WHERE
        AND (B.GenerationNo < O.BranchGenerations)
        AND (B.Complete = 1 OR X.SuitableProbes = O.MultipliedFactor)        
) AS B