2012-10-30 147 views
1

下面是一些細節,我試圖做一個SQLFiddle,但我不斷收到我的變量的錯誤。這在Sql Server 2008中有效。我的問題是,我怎樣才能讓我的查詢更快?我知道我在這裏做了許多錯誤的事情(重複的nester查詢),我希望能讓某人看一看,並幫助我從30分鐘的執行時間中解脫出來。 :-S我能做些什麼來加速這個SQL查詢?

查詢背後的基本思想是,在遊戲中我想找到所有在一段時間內沒有移動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 
+1

對於性能調整問題,您可能會在dba.stackexchange.com上得到更好的答案 – RichardTheKiwi

+0

@RichardTheKiwi - 謝謝,我也會在那裏發帖。 – Faraday

回答

3

的我要做的第一件事是實現CTE,因爲它在事物的整體模式中使用了4次。

這將意味着移動一些代碼並使用#temp表代替順序CTE。由於可以對#temp表進行聚類,併爲JOIN創建有用的索引,因此它的性能也會提高一個數量級。

參見this SQLFiddle,其顯示CTE可以被評估多次,每次參考一次。

+0

實現它意味着什麼?你有什麼機會可以幫我一把嗎?我並沒有完全將自己的頭腦包裹在代碼中,移動很多東西可能會導致比我能處理的更多的錯誤! – Faraday

+0

感謝您的建議,將嵌套的CTE更改爲臨時表,將執行時間縮短到更易於管理的30秒:) – Faraday

相關問題