0

私のクエリ(下記)は機能しません。なぜそれが機能しないのかはわかりますが、修正するのに助けが必要です。基本的に私は次のことをしようとしています:

FireEventからすべての行を取得するFireEventから-5/5秒以内にあるHitEventからすべての行を取得します。ただし、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に参加する必要があるのは1回だけであり、最終結果はCTEから直接選択できると確信しています。

ヒットイベントとファイアイベントをどのように一致させるかについては、あまり具体的ではありません。ヒットイベントを2回表示したくないので、HitEventIDでパーティション化します。2つのイベント時間の差を最小限に抑える方法でペアリングすることを想定しています。したがって、2つのイベントの差の絶対値で並べ替え、FireEventIDでタイを解除します。

編集-HitResultフィルターをwhere句から外部結合句に移動しました。
EDIT2-一致しないすべてのFireEventを保持するためにfinalwhere句に追加されます(HitEventIDはnullです)

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フィルターが最後から欠落していたことでした。また、選択リストのHitRankをHitResultに置き換えました。

必須ではありませんが、文字列リテラルではなく、プレーヤーの演習IDを火災イベントの演習IDと比較しました。これにより、文字列リテラルがクエリに1回だけ表示されるようになったため、将来的に演習IDを簡単に変更できます。

私はCTE構文を好みますが、あなたが好むように、FROM句にインラインビュー構文を保持しました。

EDIT2-一致しないすべてのFireEventを保持するためにfinalwhere句に追加されます(HitEventIDはnullです)

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 に答える