计算连续两列或多列超过某个值的位置[篮球,双倍双倍,三倍双倍]


20

我玩篮球游戏,它允许将其统计信息输出为数据库文件,因此可以从中计算出在游戏中未实现的统计信息。到目前为止,我可以很轻松地计算出我想要的统计数据,但是现在我遇到了一个问题:从一个赛季的比赛统计数据中计算出一个球员本赛季创造的双打和/或双打的数量。

双精度双精度和三重精度双精度的定义如下:

双双:

双打被定义为一种表现,其中玩家在游戏中累积五个统计类别中的两个统计的两位数总数(点,篮板,助攻,抢断和盖帽)。

三双:

三重双打被定义为一种表现,其中玩家在游戏中五个统计类别中的三个统计点(得分,篮板,助攻,抢断和盖帽)中累计两位数。

四重双(为清楚起见添加)

四重双打是指在游戏中,玩家在五个统计类别中的四个统计类别中(得分,篮板,助攻,抢断和盖帽)累计两位数的表现。

“ PlayerGameStats”表存储了玩家玩的每个游戏的统计信息,外观如下:

CREATE TABLE PlayerGameStats AS SELECT * FROM ( VALUES
  ( 1, 1,  1, 'Nuggets',    'Cavaliers',  6,  8,  2, 2,  0 ),
  ( 2, 1,  2, 'Nuggets',     'Clippers', 15,  7,  0, 1,  3 ),
  ( 3, 1,  6, 'Nuggets', 'Trailblazers', 11, 11,  1, 2,  1 ),
  ( 4, 1, 10, 'Nuggets',    'Mavericks',  8, 10,  2, 2, 12 ),
  ( 5, 1, 11, 'Nuggets',       'Knicks', 23, 12,  1, 0,  0 ),
  ( 6, 1, 12, 'Nuggets',         'Jazz',  8,  8, 11, 1,  0 ),
  ( 7, 1, 13, 'Nuggets',         'Suns',  7, 11,  2, 2,  1 ),
  ( 8, 1, 14, 'Nuggets',        'Kings', 10, 15,  0, 3,  1 ),
  ( 9, 1, 15, 'Nuggets',        'Kings',  9,  7,  5, 0,  4 ),
  (10, 1, 17, 'Nuggets',      'Thunder', 13, 10, 10, 1,  0 )
) AS t(id,player_id,seasonday,team,opponent,points,rebounds,assists,steals,blocks);

我要实现的输出如下所示:

| player_id |    team | doubleDoubles | tripleDoubles |
|-----------|---------|---------------|---------------|
|         1 | Nuggets |             4 |             1 |

到目前为止,我发现的唯一解决方案是如此糟糕,这使我感到恶心...; o)...看起来像这样:

SELECT 
  player_id,
  team,
  SUM(CASE WHEN(points >= 10 AND rebounds >= 10) OR
               (points >= 10 AND assists  >= 10) OR
               (points >= 10 AND steals   >= 10) 
                THEN 1 
                ELSE 0 
      END) AS doubleDoubles
FROM PlayerGameStats
GROUP BY player_id

……现在您读完这篇文章后可能还会呕吐(或大笑)。我什至没有写出获得所有双精度双精度组合所需的所有内容,并且省略了三精度双精度示例的case语句,因为这更加荒谬。

有一个更好的方法吗?我拥有的表结构或新的表结构(我可以编写脚本来转换表)。

我可以使用MySQL 5.5或PostgreSQL 9.2。

这是到SqlFiddle的链接,其中包含示例数据和我上面发布的糟糕的解决方案:http ://sqlfiddle.com/#!2/af6101/3

请注意,根据我所知,我对四重双注并不是很感兴趣,因为它们不会出现在我玩的游戏中,但是如果查询很容易扩展而又无需大量重写帐户,那将是一个加号。对于四双。

Answers:


10

不知道这是否是最好的方法。我首先进行选择以找出统计信息是否为两位数,如果是,则将其分配为1。总结所有这些,以找出每局两位数的总数。从那里只是总结所有的双打和三重。似乎可以工作

select a.player_id, 
a.team, 
sum(case when a.doubles = 2 then 1 else 0 end) as doubleDoubles, 
sum(case when a.doubles = 3 then 1 else 0 end) as tripleDoubles
from
(select *, 
(case when points > 9 then 1 else 0 end) +
(case when rebounds > 9 then 1 else 0 end) +
(case when assists > 9 then 1 else 0 end) +
(case when steals > 9 then 1 else 0 end) +
(case when blocks > 9 then 1 else 0  end) as Doubles
from PlayerGameStats) a
group by a.player_id, a.team

嗨,谢谢您的解决方案。我很喜欢。完全满足我的需求,并且可以轻松扩展以包括四重双和五重双,而无需过多编写。暂时将其作为接受的答案。:)
keth

我喜欢您的代码,但是您可以将其修改得更短。CASE由于布尔表达式为true时为1,为false时为0,因此无需使用语句。我已将其添加到下面的答案中,并向您大喊大叫,因为无法在此处发表完整的SQL代码块。
Joshua Huber 2014年

谢谢约书亚。完全忽略了它,看起来好多了。
SQLChao 2014年

1
@JoshuaHuber对,但是该查询仅在MySQL中有效。使用CASESUM/COUNT允许它在Postgres上也可以使用。
ypercubeᵀᴹ

@ypercube:实际上,将布尔值相加在Postgres中也有效。您只需要显式转换。但是CASE通常要快一点。我添加了一个带有其他一些小的改进的演示。
Erwin Brandstetter 2014年

7

试试看(在MySQL 5.5上为我工作):

SELECT 
  player_id,
  team,
  SUM(
    (   (points   >= 10)
      + (rebounds >= 10)
      + (assists  >= 10)
      + (steals   >= 10)
      + (blocks   >= 10) 
    ) = 2
  ) double_doubles,
  SUM(
    (   (points   >= 10)
      + (rebounds >= 10)
      + (assists  >= 10)
      + (steals   >= 10)
      + (blocks   >= 10) 
    ) = 3
  ) triple_doubles
FROM PlayerGameStats
GROUP BY player_id, team

甚至更短,通过大胆地从JChao的答案中窃取JChao的代码,但取出不需要的 CASE语句,因为当{True,False}时,布尔值expr的值为{1,0}:

select a.player_id, 
a.team, 
sum(a.doubles = 2) as doubleDoubles, 
sum(a.doubles = 3) as tripleDoubles
from
(select *, 
(points > 9) +
(rebounds > 9) +
(assists > 9) +
(steals > 9) +
(blocks > 9) as Doubles
from PlayerGameStats) a
group by a.player_id, a.team

基于以上评论,以上代码无法在PostgreSQL中运行,因为它不喜欢布尔+布尔。我还是不喜欢CASE。这是在PostgreSQL(9.3)上的一种出路,方法是强制转换为int

select a.player_id, 
a.team, 
sum((a.doubles = 2)::int) as doubleDoubles, 
sum((a.doubles = 3)::int) as tripleDoubles
from
(select *, 
(points > 9)::int +
(rebounds > 9)::int +
(assists > 9)::int +
(steals > 9)::int +
(blocks > 9)::int as Doubles
from PlayerGameStats) a
group by a.player_id, a.team

@ypercube,很好,谢谢。刚刚要求对上述问题作确切的澄清。语义学。我相信曲棍球的四个进球仍然被认为是“拉帽子戏法”,但是保龄球连续打四球可能不被认为是“火鸡”,而是“四重奏”。我不是每个游戏的语义专家。您做出决定,然后选择=还是>=合适。
Joshua Huber 2014年

感谢您的解决方案。明确地做我想要的。也像您提供的JChao的简化版本。
keth 2014年

1
不过,请记住,在PostgreSQL中无法添加布尔值。
Craig Ringer 2014年

@CraigRinger-感谢您指出这一点。由于在一般SQL方面,尤其是PostgreSQl方面,我仍然不为所动,这对我来说是宝贵的信息。:)
keth

1
@CraigRinger不错,但是我不认为MySQL支持CAST(... AS int)stackoverflow.com/questions/12126991/…)。MySQL可以做到CAST(... AS UNSIGNED),它可以在该查询中使用,但PostgreSQL不能。不确定CAST两者是否都具有可移植性。最糟糕的情况是,CASE如果便携性至高无上,最终可能会陷入困境。
Joshua Huber 2014年

6

这是对这个问题的另一种看法。

从我的角度来看,您实际上是在处理当前问题的数据透视,因此首先要做的就是取消透视。不幸的是PostgreSQL没有提供好的工具来做到这一点,因此如果不进入PL / PgSQL的动态SQL生成,我们至少可以做到:

SELECT player_id, seasonday, 'points' AS scoretype, points AS score FROM playergamestats
UNION ALL
SELECT player_id, seasonday, 'rebounds' AS scoretype, rebounds FROM playergamestats
UNION ALL
SELECT player_id, seasonday, 'assists' AS scoretype, assists FROM playergamestats
UNION ALL
SELECT player_id, seasonday, 'steals' AS scoretype, steals FROM playergamestats
UNION ALL
SELECT player_id, seasonday, 'blocks' AS scoretype, blocks FROM playergamestats

尽管这肯定不是很漂亮,但它使数据具有更大的延展性。在这里,我假设(player_id,seasonday)足以唯一地标识玩家,即,玩家ID在各个团队之间都是唯一的。如果不是,则需要包括足够的其他信息以提供唯一的密钥。

有了这些未透视的数据,现在就可以以有用的方式过滤和汇总数据,例如:

SELECT
  player_id,
  count(CASE WHEN doubles = 2 THEN 1 END) AS doubledoubles,
  count(CASE WHEN doubles = 3 THEN 1 END) AS tripledoubles
FROM (
    SELECT
      player_id, seasonday, count(*) AS doubles
    FROM
    (
        SELECT player_id, seasonday, 'points' AS scoretype, points AS score FROM playergamestats
        UNION ALL
        SELECT player_id, seasonday, 'rebounds' AS scoretype, rebounds FROM playergamestats
        UNION ALL
        SELECT player_id, seasonday, 'assists' AS scoretype, assists FROM playergamestats
        UNION ALL
        SELECT player_id, seasonday, 'steals' AS scoretype, steals FROM playergamestats
        UNION ALL
        SELECT player_id, seasonday, 'blocks' AS scoretype, blocks FROM playergamestats
    ) stats
    WHERE score >= 10
    GROUP BY player_id, seasonday
) doublestats
GROUP BY player_id;

这远非好看,而且可能没有那么快。不过,它是可维护的,只需进行最小的更改即可处理新类型的统计信息,新列等。

因此,与其说是认真的建议,不如说是“嘿,您想到了”。目标是对SQL建模,使其尽可能直接地与问题陈述相对应,而不是使其快速发展。


通过在面向MySQL的SQL中使用合理的多值插入和ANSI引用,可以大大简化此操作。谢谢; 不用一次看到反引号了。我需要更改的只是合成密钥生成。


这就是我的想法。
Colin't Hart 2014年

1
感谢您发布此解决方案。如果我在实现上述类似@ Colin'tHart的问题时遇到了问题(以前从未做过类似的事情,但是对于我将来可能要计算的其他某些统计数据似乎毫无用处)。有趣的是,有多少种方法可以完成我想要的输出。今天绝对学到了很多东西。
凯斯(Keth)2014年

1
要了解更多信息,explain analyze请查询计划(或等效的MySQL)并弄清楚它们的全部功能以及操作方法:)
Craig Ringer 2014年

@CraigRinger-谢谢。好建议。实际上,到目前为止提供的所有解决方案都可以做到这一点(我使用SqlFiddles“视图执行计划”)。但是我绝对需要在读取输出时进行“弄清楚它们都做什么以及如何做”部分的工作。= O
keth

6

什么@Joshua显示为MySQL,Postgres里的工作为好。Boolean值可以强制转换integer并累加。不过,类型转换必须明确。编写非常短的代码:

SELECT player_id, team
     , count(doubles = 2 OR NULL) AS doubledoubles
     , count(doubles = 3 OR NULL) AS tripledoubles
FROM  (
   SELECT player_id, team,
          (points   > 9)::int +
          (rebounds > 9)::int +
          (assists  > 9)::int +
          (steals   > 9)::int +
          (blocks   > 9)::int AS doubles
   FROM playergamestats
   ) a
GROUP  BY 1, 2;

但是,CASE尽管较为冗长,但速度通常要快一点。而且更便携,如果那很重要的话:

SELECT player_id, team
     , count(doubles = 2 OR NULL) AS doubledoubles
     , count(doubles = 3 OR NULL) AS tripledoubles
FROM  (
   SELECT player_id, team,
          CASE WHEN points   > 9 THEN 1 ELSE 0 END +
          CASE WHEN rebounds > 9 THEN 1 ELSE 0 END +
          CASE WHEN assists  > 9 THEN 1 ELSE 0 END +
          CASE WHEN steals   > 9 THEN 1 ELSE 0 END +
          CASE WHEN blocks   > 9 THEN 1 ELSE 0 END AS doubles
   FROM playergamestats
   ) a
GROUP  BY 1, 2;

SQL提琴。



2

使用整数除法和二进制强制转换

SELECT player_id
     , team
     , SUM(CASE WHEN Doubles = 2 THEN 1 ELSE 0 END) DoubleDouble
     , SUM(CASE WHEN Doubles = 3 THEN 1 ELSE 0 END) TripleDouble
FROM   (SELECT player_id
             , team
             , (BINARY (points DIV 10) > 0)
             + (BINARY (rebounds DIV 10) > 0)
             + (BINARY (assists DIV 10) > 0)
             + (BINARY (steals DIV 10) > 0)
             + (BINARY (blocks DIV 10) > 0)
             AS Doubles
        FROM   PlayerGameStats) d
GROUP BY player_id, team

1

我只想在这里偶然留下@Craig Ringers版本的变体,也许对将来的某个人有用。

代替多个UNION ALL,它使用unnest和array。灵感来源:https//stackoverflow.com/questions/1128737/unpivot-and-postgresql


SELECT
  player_id,
  count(CASE WHEN doubles = 2 THEN 1 END) AS doubledoubles,
  count(CASE WHEN doubles = 3 THEN 1 END) AS tripledoubles
FROM (
    SELECT
      player_id, seasonday, count(*) AS doubles
    FROM
    (
        SELECT 
          player_id, 
          seasonday,
          unnest(array['Points', 'Rebounds', 'Assists', 'Steals', 'Blocks']) AS scoretype,
          unnest(array[Points, Rebounds, Assists, Steals, Blocks]) AS score
        FROM PlayerGameStats
    ) stats
    WHERE score >= 10
    GROUP BY player_id, seasonday
) doublestats
GROUP BY player_id;

SQL小提琴:http://sqlfiddle.com/#!12 / 4980b / 3

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.