我能做些什么来加速这个 SQL 查询?

Far*_*day 5 performance sql-server-2008 sql-server tuning if-not-exists query-performance

我在@Dems 的帮助下创建了这个 SQL 查询 :-)

这是一些细节,我试图制作一个 SQLFiddle,但我的变量一直出错......这在 Sql Server 2008 中有效......我的问题是,如何使我的查询更快?我知道我在这里做错了很多事情(重复嵌套查询),我希望让你们中的一位 SQL 专家来看看并帮助我从 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
Run Code Online (Sandbox Code Playgroud)

编辑

通过更改我的查询以使用临时表而不是嵌套的 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
Run Code Online (Sandbox Code Playgroud)

小智 0

可以提高性能的方法是使用 PlayerID 和 MasterSeqID 键在 #sequenced 上创建索引。

使用键 FiringPlayerID 和包含的列([HitPlayerID]、[FiringTime])在事件上创建索引

对于以下加入:

 INNER JOIN Player ON Player.PlayerID = sequenced.PlayerID
Run Code Online (Sandbox Code Playgroud)

将其更改为以下内容,让我们看看会发生什么:

 INNER LOOP JOIN Player ON Player.PlayerID = sequenced.PlayerID
Run Code Online (Sandbox Code Playgroud)

要记录每个语句的执行时间,请使用以下命令:

DECLARE @StartTime DATETIME. @Message NVARCHAR(MAX)
Run Code Online (Sandbox Code Playgroud)

运行前语句:

SET @StartTime = GETDATE
Run Code Online (Sandbox Code Playgroud)

语句完成后放置:

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

现在您将了解每个部分需要多长时间运行。