web-dev-qa-db-ja.com

行内の2つ以上の列が特定の値を超えているところを数える[バスケットボール、ダブルダブル、トリプルダブル]

統計をデータベースファイルとして出力できるバスケットボールゲームをプレイしているため、ゲームに実装されていない統計を計算することができます。これまでのところ、必要な統計を計算するのに問題はありませんでしたが、今度は問題が発生しました。シーズン中にプレーヤーが作成したダブルダブルまたはトリプルダブルの数をゲームの統計からカウントすることです。

ダブルダブルとトリプルダブルの定義は次のとおりです。

Double-double:

ダブルダブルとは、プレーヤーがゲーム内の5つの統計カテゴリ(ポイント、リバウンド、アシスト、スチール、ブロックされたショット)の2つに合計2桁の数字を蓄積するパフォーマンスのことです。

トリプルダブル:

トリプルダブルとは、プレーヤーがゲーム内の5つの統計カテゴリ(ポイント、リバウンド、アシスト、スチール、ブロックショット)の3つに合計2桁の数字を蓄積するパフォーマンスのことです。

Quadruple-double(説明のために追加)

Quadruple-doubleは、プレーヤーがゲーム内の5つの統計的カテゴリー(ポイント、リバウンド、アシスト、スチール、ブロックされたショット)の4つで合計2桁の数字を累積するパフォーマンスとして定義されます。

「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

...そして、あなたはおそらくこれを読んだ後、おそらくあなたはまた、つまんでいる(または激しく笑っている)。私はすべてのdouble doubleの組み合わせを取得するために必要なすべてを書き出さず、トリプルdoubleのcaseステートメントをさらにばかげているので省略しました。

これを行うより良い方法はありますか?私が持っているテーブル構造または新しいテーブル構造のどちらかを使用します(テーブルを変換するスクリプトを作成できます)。

MySQL 5.5またはPostgreSQL 9.2を使用できます。

ここに、サンプルデータと上に投稿した私のひどい解決策を含むSqlFiddleへのリンクがあります。 http://sqlfiddle.com/#!2/af6101/

私が知っている限り、私がプレイするゲームでは4重ダブル(上記を参照)は発生しないので、私は本当に興味がないことに注意してください。 quadruple-doublesの場合。

20
keth

これが最善の方法かどうかはわかりません。最初に、統計が2桁かどうかを確認するために選択を行い、2桁の場合は1を割り当てました。これらすべてを合計して、ゲームごとの2桁の合計数を調べます。そこから、すべてのダブルとトリプルを合計します。動作するようです

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
10
SQLChao

これを試してください(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のコードを露骨に引きはがして、不要なCASEステートメントを取り除くことにより、ブール値exprは{True、False}のときに{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
7
Joshua Huber

これが問題の別の見方です。

私の考えでは、あなたは基本的に現在の問題のピボットされたデータを操作しているので、最初に行うべきことはアンピボットすることです。残念なことに、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を高速化するのではなく、問題のステートメントにできるだけ直接対応するようにSQLをモデル化することでした。


これは、MySQL指向のSQLで適切な多値挿入とANSIクォートを使用することにより、非常に簡単になりました。ありがとうございました;バックティックを一度も見ないのはいいことです。変更しなければならないのは、合成キーの生成だけでした。

6
Craig Ringer

@ 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フィドル。

6

整数除算とバイナリキャストの使用

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
2
Serpiton

偶然見つけた@Craig Ringersバージョンのバリエーションをここに残したいだけなのですが、将来の誰かに役立つかもしれません。

複数のUNION ALLではなく、ネストと配列を使用します。インスピレーションのソース: 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/

1
keth