@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
したがって、23秒に短縮し、16秒より長くなることを期待している場合は、#sequenced(EventTime)のインデックスおよび/またはEvents(FiringTime)のインデックスを検討してください。
テストする価値があるかもしれません。
パフォーマンスを改善できるのは、キー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
そして、各パーツの実行にかかる時間を確認します。