2012-04-20 33 views
1

我想分析一些棒球統計數據,並且在實現看起來應該是一件簡單的任務時遇到了一些麻煩。看看下面的結果集:正在使用我ROW_NUMBER()OVER(ORDER BY GAME_PK,REC_SEQ)產生計算SQL中的棒球統計數據

GAME_PK REC_SEQ BatterId PlayNumber EventType 
287576 6  462101 1   single 
287576 14  519048 2   single 
287576 25  435079 3   strikeout 
287576 26  435079 4   stolen_base_home 
287576 28  435079 5   stolen_base_2b 

的PlayNumber列。其餘部分直接來自MLB統計數據庫。 REC_SEQ是遊戲中事件的序列號。 EventType實質上是一個at-bat的結果。

我希望PlayNumber僅在BatterId更改時增加。但它必須尊重REC_SEQ的順序。所以我不認爲我可以使用RANK或DENSE_RANK,但這些似乎非常接近我所需要的。

我想我的結果集看起來像這樣:

GAME_PK REC_SEQ BatterId PlayNumber EventType 
287576 6  462101 1   single 
287576 14  519048 2   single 
287576 25  435079 3   strikeout 
287576 26  435079 3   stolen_base_home 
287576 28  435079 3   stolen_base_2b 

任何幫助表示讚賞。

謝謝!編輯:在遊戲中,擊球手可能會出現超過一次。每次出現他都應該分配一個新的PlayNumber。基本上,每個新的at-bat都需要一個新的PlayNumber。

回答

1

編輯:看起來這可以實現的唯一方式是要弄清楚其中每個組的開始和通過確定哪個順序記錄共享batterId結束。這是通過將記錄本身與1 rownum抵消來確定每個組開始的位置。一旦我們有了各組(GroupSets)的開始的集合,我們可以判斷哪個組的每個單獨的記錄屬於產生正確的編號:

with GroupSets as (
select 
    row_number() over (order by s1.rec_seq) as rownum, 
    s1.game_pk, s1.rec_seq, s1.batterid, s2.batterid as nextbatterid, 
    s1.eventtype 
from (select *, row_number() over (order by rec_seq) as rownum from stats) s1 
left join (select rec_seq, batterid, 
      row_number() over (order by rec_seq) as rownum from stats) s2 
    on s1.rownum = s2.rownum + 1 
where s1.batterid != s2.batterid or s2.batterid is null 
) 
select 
    game_pk, 
    rec_seq, 
    batterid, 
    (select max(rownum) from GroupSets gs where gs.Rec_Seq <= s1.rec_seq) as PlayNumber, 
    eventtype 
from 
    stats s1; 

演示:http://www.sqlfiddle.com/#!3/a5e68/50


舊代碼不處理交織:

實際上DENSE_RANK()函數應該這樣做。但是,我們需要對每個BatterId組的MIN(REC_SEQ)的值進行排名,以便使用REC_SEQ來控制訂單。像這樣的東西應該這樣做:

select 
    s1.game_pk, 
    s1.rec_seq, 
    s1.batterID, 
    dense_rank() over (order by s2.rec_seq) as PlayNumber, 
    s1.EventType 
from 
    stats s1 
join 
    (select batterid, min(rec_seq) rec_seq 
    from stats group by batterid) s2 on s1.batterid = s2.batterid 
order by 
    rec_seq 

演示:http://www.sqlfiddle.com/#!3/0682e/4

+0

因爲我沒有你的源數據,我假裝你張貼的樣本是源數據。您應該能夠將這個概念適用於您自己的數據源。我在這裏還假定'BatterId'值不是交錯的,即所有相同的BatterId值在'REC_SEQ'排序中一起出現。如果情況並非如此,那麼這種情況就沒有定義,即它們是否應該採用相同的PlayNumber值,或者獲得指定的新值? – mellamokb 2012-04-20 17:18:34

+0

BatterId值是交錯的。所以同樣的擊球手可以在比賽後期出現,並且在那時應該被分配一個新的PlayNumber。我基本上希望每次擊球手都能分配一個數字。 – 2012-04-20 17:33:39

+1

這已經成倍地變得更加困難,但我認爲我有它:http://www.sqlfiddle.com/#!3/a5e68/50 :)基本上必須找出組的開始和結束位置,方法是將每條記錄與下面的記錄來看看它是否是相同的batterid。然後,一旦我們確定了這些組,就可以通過查找相應的識別該特定組結束的「REC_SEQ」來確定每個記錄屬於哪個組。 – mellamokb 2012-04-20 17:51:44

0

這是很難的,但有可能在SQL Server中。我會注意到,Oracle的分析功能使這更容易。

的想法是這樣的:

  • 添加了嚴格的序列號,以便您可以在每場比賽
  • 使用此得到以前的發揮一個標誌添加到第一次玩是在蝙蝠一個給定的順序
  • 在遊戲中枚舉這些獲得播放數
  • 加入這一回的統計數據與相應的組被分配權數打

我認爲下面的代碼做的伎倆:

with s_enum as 
(
    select s.*, ROW_NUMBER() over (partition by game_pk order by rec_seq) as Seq 
    from stats 
) s_cp as 
(
    select s.*, ROW_NUMBER() over (partition by game_pk, FirstInSeq) as BattingSeq 
    from 
    (
    select s.*, 
     (case when prev.BatterId = curr.BatterId then 1 else 0 end) as FirstInSeq 
    from s_enum curr 
    left outer join s_enum prev 
     on curr.game_pk = prev.game_pk 
     and curr.Seq = prev.Seq + 1 
    ) 
) 
select s.game_pk, s.batterid, s.rec_seq, MAX(bs.req_sec) as PlayNumber  
from stats s 
join 
(
    select s.* 
    from s_cp s 
    where FirstInSeq = 1 
) bs 
    on s.game_pk = bs.game_pk 
    and s.batterid = bs.batterid 
    and s.rec_seq >= bs.req_sec 
group by s.game_pk, s.batterid, s.rec_seq 
+0

「Oracle的分析功能使這變得更容易。」 SQL Server 2012支持「lead」,「lag」,「first_value」和「last_value」。如果你的意思是任何這些功能,那麼有一個解決方案也是有趣的。如果OP尚未使用該版本,則可能會選擇切換到SQL Server 2012。 – 2012-04-20 18:31:13