0

我的查询(如下)不起作用。我有点知道为什么它不起作用,但我需要帮助修复它。基本上我正在尝试执行以下操作:

从 FireEvent 获取所有行 从 HitEvent 获取在 FireEvent 的 -5/5 秒内的所有行,如果 HitEvent 已经“配对”了一个 FireEvent,那么我不想再次包含它。基本上,我不希望任何 HitEvent 多次出现在我的查询中!我在 SQL 聊天室中被告知要使用 RANK,如果我能弄清楚如何在我的子查询中包含 FireEvent 中的 EventTime,它似乎会起作用......

无论如何,这是查询...

SELECT FireEvent.ExerciseID, 
       FireEvent.FireEventID, 
       tempHitEvent.HitEventID, 
       FireEvent.AssociatedPlayerID, 
       tempHitEvent.AssociatedPlayerID, 
       FireEvent.EventTime, 
       tempHitEvent.EventTime, 
       FireEvent.Longitude, 
       FireEvent.Latitude, 
       tempHitEvent.Longitude, 
       tempHitEvent.Latitude, 
       tempHitEvent.HitResult, 
       FireEvent.AmmunitionCode, 
       FireEvent.AmmunitionSource, 
       FireEvent.FireEventID, 
       0 AS 'IsArtillery' 
FROM   FireEvent 
       LEFT JOIN (SELECT HitEvent.*, 
                         FireEvent.FireEventID, 
                         Rank() 
                           OVER ( 
                             ORDER BY HitEvent.EventTime) AS RankValue 
                  FROM   HitEvent 
                         INNER JOIN FireEvent 
                                 ON FireEvent.EventTime BETWEEN 
                                    Dateadd(millisecond, -5000, 
                                    HitEvent.EventTime) AND 
                                               Dateadd(millisecond, 
                                               5000, HitEvent.EventTime) AND HitEvent.FiringPlayerID = FireEvent.PlayerID 
                   AND HitEvent.AmmunitionCode = 
                       FireEvent.AmmunitionCode
                   AND HitEvent.ExerciseID = 
                       'D289D508-1479-4C17-988C-5F6A847AE51E' 
                        AND FireEvent.ExerciseID = 
                       'D289D508-1479-4C17-988C-5F6A847AE51E' 
                   AND HitEvent.HitResult NOT IN ( 0, 1 ) ) AS 
                 tempHitEvent 
              ON ( 
              RankValue = 1
            AND tempHitEvent.FireEventID = 
                     FireEvent.FireEventID 
                     )
WHERE  FireEvent.ExerciseID = 'D289D508-1479-4C17-988C-5F6A847AE51E' 
ORDER BY HitEventID
4

4 回答 4

1

假设我没有引入错误,我相信以下内容将满足您的要求。我无法测试代码,但我相信我的概念是正确的。很容易出现错字或其他一些您必须追踪的愚蠢错误。但我相信您只需要在 CTE 中加入一次,就可以直接从 CTE 中选择最终结果。

对于如何将命中事件与火灾事件匹配,您并不是很具体。您永远不希望命中事件出现两次,因此我按 HitEventID 进行分区。我假设您希望以最小化两个事件时间之间差异的方式对它们进行配对。因此,我按两个事件之间差异的绝对值排序,并通过 FireEventID 打破任何平局。

编辑 - 将 HitResult 过滤器从 where 子句移到外连接子句。
EDIT2 - 附加到最终 where 子句以保留所有不匹配的 FireEvents(HitEventID 为空)

with RankedHits as (
  select F.ExcerciseID,
         F.FireEventID,
         H.HitEventID,
         F.AssociatedPlayerID as FireAssociatedPlayerID,
         H.AssociatedPlayerID as HitAssociatedPlayerID,
         F.EventTime as FireEventTime,
         H.EventTime as HitEventTime,
         F.Longitude as FireLongitude,
         F.Latitude as FireLatitude,
         H.Longitude as HitLongitude,
         H.Latitude as HitLatitude,
         H.HitResult,
         F.AmmunitionCode,
         F.AmmunitionSource,
         F.FireEventID,
         rank() over( partition by HitEventID
                      order by abs( datediff( ms, H.EventTime, F.EventTime ) ),
                               F.FireEventID
                    ) as HitRank
    from FireEvent F
    left join HitEvent H
      on F.AmmunitionCode = H.AmmunitionCode
     and F.PlayerID = H.FiringPlayerID
     and F.ExcerciseID = H.ExcerciseID
     and H.HitResult not in( 0, 1 )
     and F.EventTime between dateadd(millisecond, -5000, H.EventTime)
                         and dateadd(millisecond,  5000, H.EventTime)
   where F.ExcerciseID = 'D289D508-1479-4C17-988C-5F6A847AE51E'
)
select ExcerciseID,
       FireEventID,
       HitEventID,
       FireAssociatedPlayerID,
       HitAssociatedPlayerID,
       FireEventTime,
       HitEventTime,
       FireLongitude,
       FireLatitude,
       HitLongitude,
       HitLatitude,
       HitResult,
       AmmunitionCode,
       AmmunitionSource,
       FireEventID,
       0 as IsArtillery
  from RankedHits
 where HitRank=1 or HitEventID is null

编辑回应评论

这是从您的评论链接派生的固定代码。关键错误是从最后缺少 HitRank=1 过滤器。还在选择列表中用 HitResult 替换了 HitRank。

没有必要,但我还将玩家练习 ID 与火灾事件练习 ID 进行了比较,而不是字符串文字。这使得将来更改练习 ID 变得更容易,因为字符串文字现在只在查询中出现一次。

我更喜欢 CTE 语法,但我在 FROM 子句中保留了您似乎更喜欢的内联视图语法。

EDIT2 - 附加到最终 where 子句以保留所有不匹配的 FireEvents(HitEventID 为空)

INSERT #Events (
        exerciseid,
        fireeventid,
        hiteventid,
        firingplayerid,
        hitplayerid,
        firingplayerunitid,
        hitplayerunitid,
        firingplayerassociatedplayerid,
        hitplayerassociatedplayerid,
        firingtime,
        hittime,
        firinglongitude,
        firinglatitude,
        hitlongitude,
        hitlatitude,
        hitresult,
        ammunitioncode,
        ammunitionsource,
        eventid,
        isartillery
)
(
  SELECT ExerciseID, FireEventID, HitEventID, FiringPlayerID, HitPlayerID,
         FiringUnitID, HitUnitID, FireAssociatedPlayerID, HitAssociatedPlayerID,
         FireEventTime, HitEventTime, FireLongitude, FireLatitude, HitLongitude,
         HitLatitude, HitRank, AmmunitionCode, AmmunitionSource, EventID, 0
  FROM (
  select FireEvent.ExerciseID,
         FireEvent.FireEventID,
         HitEvent.HitEventID,
         FireEvent.PlayerID as FiringPlayerID,
         HitEvent.PlayerID as HitPlayerID,
         FiringPlayer.UnitID as FiringUnitID,
         HitPlayer.UnitID as HitUnitID,
         FireEvent.AssociatedPlayerID as FireAssociatedPlayerID,
         HitEvent.AssociatedPlayerID as HitAssociatedPlayerID,
         FireEvent.EventTime as FireEventTime,
         HitEvent.EventTime as HitEventTime,
         FireEvent.Longitude as FireLongitude,
         FireEvent.Latitude as FireLatitude,
         HitEvent.Longitude as HitLongitude,
         HitEvent.Latitude as HitLatitude,
         HitEvent.HitResult,
         FireEvent.AmmunitionCode,
         FireEvent.AmmunitionSource,
         FireEvent.FireEventID AS 'EventID',
         rank() over( partition by HitEventID
                      order by abs( datediff( ms, HitEvent.EventTime, FireEvent.EventTime ) ),
                               FireEvent.FireEventID
                    ) as HitRank
    from FireEvent
    left join HitEvent
      on FireEvent.ExerciseID = HitEvent.ExerciseID
      and FireEvent.AmmunitionCode = HitEvent.AmmunitionCode
     and FireEvent.PlayerID = HitEvent.FiringPlayerID
     and HitEvent.HitResult not in( 0, 1 )
     and FireEvent.EventTime between dateadd(millisecond, -5000, HitEvent.EventTime)
                         and dateadd(millisecond,  5000, HitEvent.EventTime)
    LEFT JOIN Player FiringPlayer
           ON FiringPlayer.PlayerID = FireEvent.PlayerID
          AND FiringPlayer.ExerciseID = FireEvent.ExcerciseID
    LEFT JOIN Player HitPlayer
           ON HitPlayer.PlayerID = HitEvent.PlayerID
          AND HitPlayer.ExerciseID = FireEvent.ExcerciseID
   where FireEvent.ExerciseID = 'D289D508-1479-4C17-988C-5F6A847AE51E'
  ) as temp
  where HitRank=1 or HitEventID is null
)
于 2012-05-30T02:45:39.767 回答
1

这里的问题是您使用RANK()而不是ROW_NUMBER(). 如果任何 hitevent 与匹配的 fireevents 相差相同的数量,那么它们的等级均为 1,这意味着您将得到重复。根据我们在聊天中的对话,将整个查询包装在一个子查询中,现在摆脱内部子查询并进行直接连接,然后只限制到HitRank = 1外部查询,你会很成功。

于 2012-05-30T21:52:46.997 回答
0

我不确定这是否是您想要的,但您可以在查询开始时使用 CTE

  With cte as (
  SELECT FireEvent.ExerciseID, 
   FireEvent.FireEventID, 
   tempHitEvent.HitEventID, 
   FireEvent.AssociatedPlayerID, 
   tempHitEvent.AssociatedPlayerID, 
   FireEvent.EventTime, 
   tempHitEvent.EventTime, 
   FireEvent.Longitude, 
   FireEvent.Latitude, 
   tempHitEvent.Longitude, 
   tempHitEvent.Latitude, 
   tempHitEvent.HitResult, 
   FireEvent.AmmunitionCode, 
   FireEvent.AmmunitionSource, 
   FireEvent.FireEventID, 
   0 AS 'IsArtillery' 
 FROM   FireEvent )
 Select * from CTE 
   LEFT JOIN (SELECT HitEvent.*, 
                     FireEvent.FireEventID, 
                     Rank() 
                       OVER ( 
                         ORDER BY HitEvent.EventTime) AS RankValue 
              FROM   HitEvent 
                     INNER JOIN FireEvent 
                             ON FireEvent.EventTime BETWEEN 
                                Dateadd(millisecond, -5000, 
                                HitEvent.EventTime) AND 
                                           Dateadd(millisecond, 
                                           5000, HitEvent.EventTime) AND      HitEvent.FiringPlayerID = FireEvent.PlayerID 
               AND HitEvent.AmmunitionCode = 
                   FireEvent.AmmunitionCode
               AND HitEvent.ExerciseID = 
                   'D289D508-1479-4C17-988C-5F6A847AE51E' 
                    AND FireEvent.ExerciseID = 
                   'D289D508-1479-4C17-988C-5F6A847AE51E' 
               AND HitEvent.HitResult NOT IN ( 0, 1 ) ) AS 
             tempHitEvent 
          ON ( 
          RankValue = 1
        AND tempHitEvent.FireEventID = 
                 FireEvent.FireEventID 
                 )
  WHERE  FireEvent.ExerciseID = 'D289D508-1479-4C17-988C-5F6A847AE51E' 
  ORDER BY HitEventID
于 2012-05-30T01:38:18.667 回答
0

以下大大简化的示例应该会产生最佳对。考虑到当前时间,它可能会产生沙鼠,我不会注意到。

declare @Fires as Table ( FireId Int Identity, FireTime Int )
insert into @Fires ( FireTime ) values
  ( 1 ), ( 2 ), ( 3 ), ( 5 ), ( 8 ), ( 13 ), ( 21 ), ( 34 )

declare @Hits as Table ( HitId Int Identity, HitTime Int )
insert into @Hits ( HitTime ) values
  ( 5 ), ( 7 ), ( 12 ), ( 12 ), ( 20 ), ( 21 ), ( 30 )

-- Create a temporary table of all possible pairs meeting the time criteria.
select F.*, Dense_Rank() over ( partition by H.HitTime, H.HitId order by F.FireTime, F.FireId ) as [FireRank],
  H.*, Dense_Rank() over ( order by H.HitTime, H.HitId ) as [HitRank]
  into #Pairs
  from @Fires as F inner join
    @Hits as H on F.FireTime between H.HitTime - 5 and H.HitTime + 5

-- Clean up the pairs.
declare @HitRank as Int = 1
declare @MaxHitRank as Int = ( select Max( HitRank ) from #Pairs )
declare @MinFireRank as Int
while ( @HitRank <= @MaxHitRank )
  begin
  select @MinFireRank = Min( FireRank ) from #Pairs where HitRank = @HitRank
  delete from #Pairs
    where
      -- It is not the first   FireRank   for the current   HitRank .
      ( HitRank = @HitRank and FireRank > @MinFireRank ) or
      -- Once we pair a fire event don't pair it against another hit event.
      ( HitRank > @HitRank and FireId in ( select FireId from #Pairs where HitRank = @HitRank and FireRank = @MinFireRank ) )
  set @HitRank = @HitRank + 1
  end

-- Select the paired events.
select FireId, FireTime, HitId, HitTime
  from #Pairs
union
-- Add in the unpaired fire events.
select FireId, FireTime, NULL, NULL
  from @Fires
  where FireId not in ( select FireId from #Pairs )
order by FireTime, FireId, HitTime, HitId

drop table #Pairs
于 2012-05-30T05:06:35.807 回答