【问题标题】:Very slow stored procedure非常慢的存储过程
【发布时间】:2011-06-30 23:58:28
【问题描述】:

我在查询优化方面遇到了困难,目前我非常接近重新设计数据库的点。而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

【问题讨论】:

  • 已经尝试恢复,但没有 R2 unfort。表ResultsProbes 分别有多少行?
  • 我已编辑问题以显示行数
  • SqlServer Profiler 为 CPU/Reads/Writes/Duration 提供大约 6300/500000/670/8100

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


【解决方案1】:

其他人可能比我能更好地解释为什么这要快得多。经验告诉我,当您有一堆查询一起运行很慢但在各个部分应该很快时,那么值得尝试使用临时表。

这样更快

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

平均执行时间为 54 毫秒,而原始执行时间为 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 行(在这种情况下),它比以前所做的更快。

【讨论】:

  • 请原谅我滥用你的命名约定我发现当一切都被别名时,SQL 真的很难阅读。
  • 嗨大卫,这真的很奇怪,它的运行速度真的非常快,我不明白它怎么会发生......关于约定不是问题,如果你愿意,可以使用你自己的.. .
  • 也通过我的解决方案:),连接不是 100% ,它只是到所需的扩展。从而影响执行计划中的表扫描和其他...
【解决方案2】:

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

【讨论】:

    【解决方案3】:

    两个uniqueidentifier 列上的连接似乎是问题的根源。一个是聚集索引,另一个是非聚集索引(FK表)。很好,它们上有索引。不幸的是,在加入大量行时,guid 的性能是出了名的差。

    作为故障排除步骤:

    • 索引处于什么状态?上次更新统计信息是什么时候?
    • 当临时执行时,该子查询对其自身的性能如何?即当您自己运行此语句时,结果集返回的速度有多快?可以接受吗?
    • 重建 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
    

    【讨论】:

    • 所有索引都被重建和重组。每一个都有碎片
    【解决方案4】:

    以下在我的系统上运行速度提高了大约 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
    

    【讨论】:

      【解决方案5】:

      子查询插入本地临时表

      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
      

      【讨论】:

      • 除了一些语法差异之外,这与我上面给出的解决方案相同。很高兴看到我们的想法是一样的。
      • 我重复一遍,在我的情况下,连接不是 100% ,它只是到所需的扩展。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-07-23
      • 2012-06-10
      • 1970-01-01
      • 2012-10-12
      相关资源
      最近更新 更多