4

クエリの最適化に苦労しています。現在、データベースの再設計のポイントに非常に近づいています。そして、スタックオーバーフローは私の最後の希望です。クエリを表示するだけでは不十分だと思うので、手動でデータを生成したくない場合に備えて、データベース スクリプトだけでなく、添付のデータベース バックアップもリンクしました。

ここでは、スクリプトとバックアップの両方を見つけることができます

次のことをしようとすると問題が発生します...

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

EDIT:各テーブルの行数は次のとおりです。

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

5 に答える 5

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秒と比較して、平均実行時間が54ミリ秒とはるかに高速です。

編集

見て、私のアイデアを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行(この場合)の場合は、以前よりも高速になります。

于 2011-07-01T02:11:07.540 に答える
2

これは基本的に完全な推測ですが、過去に、サブクエリの結果への結合が恐ろしく遅くなる可能性があることに気付きました。つまり、実際には必要のないサブクエリが何度も評価されていました。
これを回避する方法は、サブクエリを CTE に移動し、代わりにそれらに参加することでした。幸運を!

于 2011-07-01T00:23:18.493 に答える
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
于 2011-07-01T03:57:13.227 に答える
1

2 つの列の結合がuniqueidentifier問題の原因であるようです。1 つはクラスター化されたインデックスで、もう 1 つは (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
于 2011-07-01T00:15:58.270 に答える