4

我很难与查询优化,目前我非常接近数据库重新设计的要点。而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 
+0

已尝试恢复,但没有R2不幸。 “结果”和“探针”表分别有多少行? –

+0

我编辑了问题以显示行数 – Lu4

+0

SqlServer Profiler给出的6300/500000/670/8100的CPU /读取/写入/持续时间 – Lu4

回答

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 

这是相比于6秒与原来一个54ms的平均执行时间更快。

编辑

一看,结合我与那些从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 

那么你就可以得到这个下降到15ms的是400倍,比我们开始更好。查看执行计划表明在临时表上发生了表扫描。通常情况下,尽可能避免表扫描,但对于128行(在这种情况下),它比以前做的更快。

+0

请原谅我滥用你的命名约定我发现当一切都是别名时,SQL真的很难阅读。 –

+0

嗨大卫,这是真正的奇怪,它真的很快速地工作,我不明白它怎么会发生......关于约定不是一个问题,使用你自己的,如果你想... – Lu4

+0

通过我的解决方案以及:),连接不是100%,它只是在需要的范围内。因此,在执行计划中影响表扫描和其他操作... – Pankaj

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 
+0

所有索引都被重建和重组。每个人都有分裂<20%,统计数据根本没有更新,没有理解第二步。据我了解,这两个指数应该聚集在一起还是不聚集? – Lu4

2

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

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 
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 
+0

除了一些语法差异之外,这与我上面给出的解决方案相同。很高兴看到我们都在同样的思路。 –

+0

我在重复,在我的情况下,连接不是100%,它只是在需要的范围内。 – Pankaj