计算一行中两列或更多列超过某个值的位置 [篮球、双双、三双]

20 mysql postgresql count group-by

我玩了一场篮球比赛,它允许将其统计数据输出为数据库文件,因此可以从中计算出游戏中未实现的统计数据。到目前为止,我在计算我想要的统计数据时没有遇到任何问题,但现在我遇到了一个问题:从他的比赛统计数据中计算一个球员在整个赛季中取得的双打和/或三双的数量。

双双和三双的定义如下:

双双:

两双是指球员在一场比赛中在五个统计类别中的两个类别(得分、篮板、助攻、抢断和盖帽)中累积达到两位数的总和的表现。

三双:

三双被定义为球员在一场比赛中在五个统计类别中的三个——得分、篮板、助攻、抢断和盖帽——中累计达到两位数的表现。

四双(添加澄清)

四双被定义为球员在一场比赛中在五个统计类别中的四个类别(得分、篮板、助攻、抢断和盖帽)中累计达到两位数的表现。

“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);
Run Code Online (Sandbox Code Playgroud)

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

| player_id |    team | doubleDoubles | tripleDoubles |
|-----------|---------|---------------|---------------|
|         1 | Nuggets |             4 |             1 |
Run Code Online (Sandbox Code Playgroud)

到目前为止,我发现的唯一解决方案太糟糕了,它让我呕吐......;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
Run Code Online (Sandbox Code Playgroud)

……现在您在阅读本文后可能也会呕吐(或大笑)。我什至没有写出获得所有双双组合所需的一切,并且省略了三双的 case 语句,因为它更荒谬。

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

我可以使用 MySQL 5.5 或 PostgreSQL 9.2。

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

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

小智 9

不知道这是不是最好的方法。我首先做了一个选择,以确定一个统计数据是否为两位数,如果是,则为其分配一个 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
Run Code Online (Sandbox Code Playgroud)


Jos*_*ber 6

试试这个(在 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
Run Code Online (Sandbox Code Playgroud)

或者更短,从他的答案中公然撕掉 JChao 的代码,但是去掉不需要的CASE语句,因为当 {True,False} 时 boolean 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
Run Code Online (Sandbox Code Playgroud)

基于上面的代码不会在 PostgreSQL 中运行的评论,因为不喜欢做 boolean + boolean。我还是不喜欢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
Run Code Online (Sandbox Code Playgroud)


Cra*_*ger 5

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

我的想法是,您实际上是在处理当前问题的透视数据,因此首先要做的是取消透视它。不幸的是 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
Run Code Online (Sandbox Code Playgroud)

这使数据具有更具延展性的形式,尽管它肯定不漂亮。这里我假设 (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;
Run Code Online (Sandbox Code Playgroud)

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

所以这更像是一个“嘿,你想到了吗”而不是一个严肃的建议。目标是建模 SQL 以尽可能直接地对应问题陈述,而不是使其快速。


通过在面向 MySQL 的 SQL 中使用合理的多值插入和 ANSI 引用,这变得更加容易。谢谢; 很高兴一次没有看到反引号。我只需要更改合成密钥生成。


Erw*_*ter 5

什么@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;
Run Code Online (Sandbox Code Playgroud)

但是,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;
Run Code Online (Sandbox Code Playgroud)

SQL小提琴。