web-dev-qa-db-ja.com

このSQLクエリを高速化するにはどうすればよいですか?

@Demsを利用してこのSQLクエリを作成しました:-)

ここにいくつかの詳細があります。SQLFiddleを作成しようとしましたが、変数でエラーが発生し続けました...これはSQL Server 2008で機能します...私の質問は、クエリをより速くするにはどうすればよいですか?私はここで多くの問題を犯していることを知っています(nesterクエリを繰り返します)。SQLの達人の1人に見てもらい、30分の実行時間からこれを解決できるようにしたいと思っています。 :-S

これについて私を助けるために必要な情報の正確な量はわかりません。質問があれば遠慮してください。できるだけ早く返信します。詳細レベルが少し足りない場合は申し訳ありませんが、あなたが何を知りたいか、何を知りたいかわかりません。

クエリの背後にある基本的な考え方は、ゲーム内で、一定時間5ユニット移動しなかったすべてのプレーヤーを見つけたいというものです。静止している間に発砲し、動きが止まる前に60分間発砲しなかったプレイヤーです。クエリは機能しますが、AND NOT EXISTS句は、クロールまでの速度を落としていますが、実行するのに16秒かかりました。 16秒はまだ長い時間なので、他の改善はありがたいですが、今のところこれは私自身のPOCゲーム(ビットとピースを一緒に投げるだけ)なので、16秒は許容範囲です...

DECLARE @n INT , @DistanceLimit INT
SELECT  @n = 2 , @DistanceLimit = 5;

WITH    partitioned
          AS ( SELECT   * ,
                        CASE WHEN Distance < @DistanceLimit THEN 1
                             ELSE 0
                        END AS PartitionID
               FROM     EntityStateEvent
               WHERE    ExerciseID = '8B50D860-6C4E-11E1-8E70-0025648E65EC'
             ),
        sequenced
          AS ( SELECT   ROW_NUMBER() OVER ( PARTITION BY PlayerID ORDER BY EventTime ) AS MasterSeqID ,
                        ROW_NUMBER() OVER ( PARTITION BY PlayerID, PartitionID ORDER BY EventTime ) AS PartIDSeqID ,
                        *
               FROM     partitioned
             ),
        filter
          AS ( SELECT   MasterSeqID - PartIDSeqID AS GroupID ,
                        MIN(MasterSeqID) AS GroupFirstMastSeqID ,
                        MAX(MasterSeqID) AS GroupFinalMastSeqID ,
                        PlayerID
               FROM     sequenced
               WHERE    PartitionID = 1
               GROUP BY PlayerID ,
                        MasterSeqID - PartIDSeqID
               HAVING   COUNT(*) >= @n
             )
    SELECT
DISTINCT    ( sequenced.PlayerID ) ,
            MIN(sequenced.EventTime) AS StartTime ,
            MAX(sequenced.EventTime) AS EndTime ,
            DATEDIFF(minute, MIN(sequenced.EventTime),
                     MAX(sequenced.EventTime)) AS StaticTime ,
            Player.Designation AS 'Player'
    FROM    filter
            INNER JOIN sequenced ON sequenced.PlayerID = filter.PlayerID
                                    AND sequenced.MasterSeqID >= filter.GroupFirstMastSeqID
                                    AND sequenced.MasterSeqID <= filter.GroupFinalMastSeqID
            INNER JOIN Events ON Events.FiringPlayerID = sequenced.PlayerID 
            INNER JOIN Player ON Player.PlayerID = sequenced.PlayerID
                                 AND Player.Force = 'FR'
                                 AND NOT EXISTS ( SELECT    *
                                                  FROM      Events
                                                  WHERE     Events.FiringPlayerID = Player.PlayerID
                                                  GROUP BY  Events.FiringTime
                                                  HAVING    Events.FiringTime BETWEEN DATEADD(minute,
                                                              -60,
                                                              ( SELECT
                                                              MIN(s.EventTime)
                                                              FROM
                                                              sequenced s
                                                              WHERE
                                                              s.PlayerID = filter.PlayerID
                                                              AND s.MasterSeqID >= filter.GroupFirstMastSeqID
                                                              AND s.MasterSeqID <= filter.GroupFinalMastSeqID
                                                              ))
                                                              AND
                                                              ( SELECT
                                                              MIN(s.EventTime)
                                                              FROM
                                                              sequenced s
                                                              WHERE
                                                              s.PlayerID = filter.PlayerID
                                                              AND s.MasterSeqID >= filter.GroupFirstMastSeqID
                                                              AND s.MasterSeqID <= filter.GroupFinalMastSeqID
                                                              ) )
            INNER JOIN Player HitPlayer ON HitPlayer.PlayerID = Events.HitPlayerID
    WHERE   HitPlayer.[FORCE] = 'HO'
    GROUP BY GroupID ,
            sequenced.PlayerID ,
            Events.FiringPlayerID ,
            Events.FiringTime ,
            Player.Designation
    HAVING  DATEDIFF(minute, MIN(sequenced.EventTime),
                     MAX(sequenced.EventTime)) > 5
            AND Events.FiringTime BETWEEN MIN(sequenced.EventTime)
                                  AND     MAX(sequenced.EventTime)
    ORDER BY StartTime

[〜#〜]編集[〜#〜]

ネストされたCTEではなく一時テーブルを使用するようにクエリを変更することで、実行時間を23秒に短縮しました-実行計画へのリンクは次のとおりです http://www.filedropper.com/executionplan

これが今のクエリです:

DECLARE @n INT , @DistanceLimit INT
SELECT  @n = 2 , @DistanceLimit = 5;

SELECT *, CASE WHEN Distance < @DistanceLimit THEN 1
ELSE 0
END AS PartitionID
INTO #partitioned
FROM EntityStateEvent
WHERE ExerciseID = '8B50D860-6C4E-11E1-8E70-0025648E65EC'

SELECT *, ROW_NUMBER() OVER ( PARTITION BY PlayerID ORDER BY EventTime ) AS MasterSeqID ,
ROW_NUMBER() OVER ( PARTITION BY PlayerID, PartitionID ORDER BY EventTime ) AS PartIDSeqID 
INTO #sequenced
FROM #partitioned;

        WITH filter
          AS ( SELECT   MasterSeqID - PartIDSeqID AS GroupID ,
                        MIN(MasterSeqID) AS GroupFirstMastSeqID ,
                        MAX(MasterSeqID) AS GroupFinalMastSeqID ,
                        PlayerID
               FROM     #sequenced
               WHERE    PartitionID = 1
               GROUP BY PlayerID ,
                        MasterSeqID - PartIDSeqID
               HAVING   COUNT(*) >= @n
             )
    SELECT
DISTINCT    ( sequenced.PlayerID ) ,
            MIN(sequenced.EventTime) AS StartTime ,
            MAX(sequenced.EventTime) AS EndTime ,
            DATEDIFF(minute, MIN(sequenced.EventTime),
                     MAX(sequenced.EventTime)) AS StaticTime ,
            Player.Designation AS 'Player'
    FROM    filter
            INNER JOIN #sequenced sequenced ON sequenced.PlayerID = filter.PlayerID
                                    AND sequenced.MasterSeqID >= filter.GroupFirstMastSeqID
                                    AND sequenced.MasterSeqID <= filter.GroupFinalMastSeqID
            INNER JOIN Events ON Events.FiringPlayerID = sequenced.PlayerID 
            INNER JOIN Player ON Player.PlayerID = sequenced.PlayerID
                                 AND Player.Force = 'FR'
                                 AND NOT EXISTS ( SELECT    *
                                                  FROM      Events
                                                  WHERE     Events.FiringPlayerID = Player.PlayerID
                                                  GROUP BY  Events.FiringTime
                                                  HAVING    Events.FiringTime BETWEEN DATEADD(minute,
                                                              -60,
                                                              ( SELECT
                                                              MIN(s.EventTime)
                                                              FROM
                                                              #sequenced s
                                                              WHERE
                                                              s.PlayerID = filter.PlayerID
                                                              AND s.MasterSeqID >= filter.GroupFirstMastSeqID
                                                              AND s.MasterSeqID <= filter.GroupFinalMastSeqID
                                                              ))
                                                              AND
                                                              ( SELECT
                                                              MIN(s.EventTime)
                                                              FROM
                                                              #sequenced s
                                                              WHERE
                                                              s.PlayerID = filter.PlayerID
                                                              AND s.MasterSeqID >= filter.GroupFirstMastSeqID
                                                              AND s.MasterSeqID <= filter.GroupFinalMastSeqID
                                                              ) )
            INNER JOIN Player HitPlayer ON HitPlayer.PlayerID = Events.HitPlayerID
    WHERE   HitPlayer.[FORCE] = 'HO'
    GROUP BY GroupID ,
            sequenced.PlayerID ,
            Events.FiringPlayerID ,
            Events.FiringTime ,
            Player.Designation
    HAVING  DATEDIFF(minute, MIN(sequenced.EventTime),
                     MAX(sequenced.EventTime)) > 5
            AND Events.FiringTime BETWEEN MIN(sequenced.EventTime)
                                  AND     MAX(sequenced.EventTime)
    ORDER BY StartTime
5
Faraday

したがって、23秒に短縮し、16秒より長くなることを期待している場合は、#sequenced(EventTime)のインデックスおよび/またはEvents(FiringTime)のインデックスを検討してください。

テストする価値があるかもしれません。

1
Michael J Swart

パフォーマンスを改善できるのは、キーPlayerIDおよびMasterSeqIDを使用して#sequencedにインデックスを作成することです。

キーFiringPlayerIDと含まれる列([HitPlayerID]、[FiringTime])を使用してイベントのインデックスを作成します

次の結合の場合:

 INNER JOIN Player ON Player.PlayerID = sequenced.PlayerID

それを次のように変更して、何が起こるかを見てみましょう:

 INNER LOOP JOIN Player ON Player.PlayerID = sequenced.PlayerID

各ステートメントの実行時間を記録するには、以下を使用します。

DECLARE @StartTime DATETIME. @Message NVARCHAR(MAX)

ステートメントを実行する前に:

SET @StartTime = GETDATE

ステートメントが終了した後:

SET @Message = 'Time took to run '+CAST(DATEDIFF(millisecond,@StartTime,GETDATE()))    
RAISERROR(@Message,0,0) WITH NOWAIT

そして、各パーツの実行にかかる時間を確認します。

0
Farfarak