2012-04-20 29 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