Zählen Sie, wo zwei oder mehr Spalten in einer Reihe einen bestimmten Wert überschreiten [Basketball, Double Double, Triple Double]

20

Ich spiele ein Basketballspiel, bei dem die Statistiken als Datenbankdatei ausgegeben werden können, sodass man daraus Statistiken berechnen kann, die nicht im Spiel implementiert sind. Bisher hatte ich keine Probleme, die von mir gewünschten Statistiken zu berechnen, aber jetzt bin ich auf ein Problem gestoßen: Zählen der Anzahl der doppelten und / oder dreifachen Doppelten, die ein Spieler im Laufe der Saison aus seinen Spielstatistiken gemacht hat.

Die Definition eines doppelten Doppelten und eines dreifachen Doppelten lautet wie folgt:

Doppel-Doppel:

Ein Double-Double ist eine Leistung, bei der ein Spieler eine zweistellige Gesamtsumme in zwei von fünf statistischen Kategorien - Punkte, Abpraller, Vorlagen, Diebstähle und geblockte Schüsse - in einem Spiel ansammelt.

Triple-Double:

Ein Triple-Double ist eine Leistung, bei der ein Spieler eine zweistellige Gesamtsumme in drei von fünf statistischen Kategorien - Punkte, Abpraller, Vorlagen, Diebstähle und geblockte Schüsse - in einem Spiel ansammelt.

Vierfach-Doppel (zur Verdeutlichung hinzugefügt)

Ein Vierfach-Doppel ist eine Leistung, bei der ein Spieler eine zweistellige Gesamtsumme in vier von fünf statistischen Kategorien - Punkte, Abpraller, Vorlagen, Diebstähle und geblockte Schüsse - in einem Spiel ansammelt.

Die "PlayerGameStats" -Tabelle speichert Statistiken für jedes Spiel, das ein Spieler spielt, und sieht folgendermaßen aus:

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);

Die Ausgabe, die ich erreichen möchte, sieht folgendermaßen aus:

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

Die einzige Lösung, die ich bisher gefunden habe, ist so schrecklich, dass ich kotze ...; o) ... Es sieht so aus:

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

... und jetzt kotzen (oder lachen) Sie wahrscheinlich auch, nachdem Sie dies gelesen haben. Ich habe nicht einmal alles aufgeschrieben, was nötig wäre, um alle Double-Double-Kombinationen zu erhalten, und die case-Anweisung für die Triple-Double weggelassen, weil sie noch lächerlicher ist.

Gibt es einen besseren Weg, dies zu tun? Entweder mit der Tabellenstruktur, die ich habe, oder mit einer neuen Tabellenstruktur (ich könnte ein Skript schreiben, um die Tabelle zu konvertieren).

Ich kann MySQL 5.5 oder PostgreSQL 9.2 verwenden.

Hier ist ein Link zu SqlFiddle mit Beispieldaten und meiner schrecklichen Lösung, die ich oben gepostet habe: http://sqlfiddle.com/#!2/af6101/3

Beachten Sie, dass ich nicht wirklich an Vierfach-Doppel interessiert bin (siehe oben), da sie meines Wissens nicht in dem Spiel vorkommen, das ich spiele, aber es wäre ein Plus, wenn die Abfrage leicht erweiterbar wäre, ohne viel umzuschreiben für Vierbettzimmer.

keth
quelle

Antworten:

10

Ich weiß nicht, ob dies der beste Weg ist. Ich habe zuerst eine Auswahl getroffen, um herauszufinden, ob ein Wert zweistellig ist, und ihm eine 1 zugewiesen, wenn dies der Fall ist. Summiert alle diese Zahlen, um die Gesamtzahl der zweistelligen Zahlen pro Spiel zu ermitteln. Fassen Sie einfach alle Doppel- und Dreifachwerte zusammen. Scheint zu funktionieren

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
SQLChao
quelle
Hallo, danke für deine Lösung. Ich mag es wirklich. Tut genau das, was ich will und ist leicht erweiterbar, um Quadruple-Double- und Quintuple-Doubles ohne viel Schreibarbeit einzuschließen. Wird dies die akzeptierte Antwort für jetzt machen. :)
keth
Ich mag Ihren Code, aber Sie können ihn hacken, um noch kürzer zu sein. CASEAnweisungen müssen nicht verwendet werden , da boolesche Ausdrücke bei true den Wert 1 und bei false den Wert 0 annehmen. Ich habe es zu meiner Antwort unten mit einem Gruß an Sie hinzugefügt, da ich hier keinen vollständigen SQL-Codeblock im Kommentar posten kann.
Joshua Huber
Vielen Dank, Joshua. Total übersehen und es sieht viel besser aus.
SQLChao
1
@JoshuaHuber Richtig, aber dann funktioniert die Abfrage nur in MySQL. Mit CASEund SUM/COUNTkann es auch auf Postgres angewendet werden.
ypercubeᵀᴹ
@ypercube: Das Addieren von Booleschen Werten funktioniert auch in Postgres. Sie müssen nur explizit besetzen. Ist CASEaber in der Regel ein kleines bisschen schneller. Ich habe eine Demo mit ein paar anderen kleinen Verbesserungen hinzugefügt.
Erwin Brandstetter
7

Probieren Sie dies aus (bei mir mit 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

Oder noch kürzer, indem Sie JChaos Code aus seiner Antwort herausnehmen, aber die nicht benötigten CASEAnweisungen herausnehmen, da der boolesche Ausdruck {1,0} ergibt, wenn {True, False}:

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

Basierend auf den Kommentaren, dass der obige Code in PostgreSQL nicht ausgeführt wird, da er nicht gerne Boolean + Boolean ausführt. Ich mag es immer noch nicht CASE. Hier ist ein Ausweg aus 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
Joshua Huber
quelle
@ypercube, guter Punkt und danke. Hatte gerade diese genaue Klarstellung als Kommentar zur obigen Frage angefordert. Semantik. Ich glaube, vier Tore im Eishockey gelten immer noch als "Hattrick", aber vier aufeinanderfolgende Schläge beim Bowling werden möglicherweise nicht als "Truthahn" angesehen, sondern als "Quad". Ich bin kein Experte für die Semantik jedes Spiels. Sie treffen die Entscheidung und wählen =oder >=als fit.
Joshua Huber
Vielen Dank für Ihre Lösung. Auf jeden Fall macht was ich will. Auch wie die von Ihnen bereitgestellte Shortend-Version von JChao.
keth
1
Das Hinzufügen von Booleschen Werten funktioniert in PostgreSQL jedoch nicht.
Craig Ringer
@CraigRinger - danke für den Hinweis. Da ich in Bezug auf SQL im Allgemeinen und PostgreSQl im Besonderen immer noch grün hinter den Ohren bin, ist dies eine wichtige Information für mich. :)
keth
1
@CraigRinger Schön, aber ich denke nicht, dass MySQL dies unterstützt CAST(... AS int) ( stackoverflow.com/questions/12126991/… ). MySQL kann das CAST(... AS UNSIGNED), was in dieser Abfrage funktioniert, aber PostgreSQL nicht. Ich CASTbin mir nicht sicher, ob es eine Gemeinsamkeit gibt , die beide für die Portabilität tun können. Schlimmster Fall, könnte CASEam Ende stecken bleiben, wenn Portabilität von größter Bedeutung ist.
Joshua Huber
6

Hier ist eine andere Sicht auf das Problem.

So wie ich das sehe, arbeiten Sie im Wesentlichen mit Pivot-Daten für das aktuelle Problem. Das erste, was Sie tun müssen, ist, die Pivot-Funktion aufzuheben. Leider bietet PostgreSQL dafür keine nützlichen Tools. Daher können wir zumindest Folgendes tun, ohne in die dynamische SQL-Generierung in PL / PgSQL einzusteigen:

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

Dies bringt die Daten in eine formbarere Form, obwohl es sicher nicht schön ist. Hier gehe ich davon aus, dass (player_id, seasonday) ausreicht, um Spieler eindeutig zu identifizieren, dh die Spieler-ID ist für alle Teams eindeutig. Ist dies nicht der Fall, müssen Sie genügend weitere Informationen angeben, um einen eindeutigen Schlüssel bereitzustellen.

Mit diesen nicht zugewiesenen Daten ist es jetzt möglich, sie auf nützliche Weise zu filtern und zu aggregieren, wie zum Beispiel:

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;

Das ist alles andere als hübsch und wahrscheinlich nicht so schnell. Es ist jedoch wartbar und erfordert nur minimale Änderungen, um mit neuen Arten von Statistiken, neuen Spalten usw. umzugehen.

Es ist also eher ein "Hey, hast du daran gedacht" als ein ernstzunehmender Vorschlag. Ziel war es, die SQL so zu modellieren, dass sie der Problemstellung so direkt wie möglich entspricht, anstatt sie zu beschleunigen.


Dies wurde durch die Verwendung vernünftiger mehrwertiger Einfügungen und ANSI-Anführungszeichen in Ihrem MySQL-orientierten SQL-Code erheblich vereinfacht. Vielen Dank; Es ist schön, einmal keine Backticks zu sehen. Alles, was ich ändern musste, war die Erzeugung synthetischer Schlüssel.

Craig Ringer
quelle
Dies ist eine Art, was ich im Sinn hatte.
Colin 't Hart
1
Danke für die Postings dieser Lösung. Hatten meine Probleme bei der Implementierung von so etwas wie @ Colin'tHart (habe noch nie so etwas gemacht, scheint aber für einige andere Statistiken, die ich möglicherweise in Zukunft berechnen möchte, sehr nützlich zu sein). Es ist interessant, wie viele Möglichkeiten es gibt, meine gewünschte Ausgabe zu erreichen. Heute definitiv viel gelernt.
keth
1
Weitere explain analyzeInformationen finden Sie in den Abfrageplänen (oder in MySQL-Entsprechungen) und finden Sie heraus, was sie alle tun und wie :)
Craig Ringer
@CraigRinger - Danke. Guter Rat. Eigentlich hat das irgendwie mit allen Lösungen geklappt (ich habe SqlFiddles "view execution plan" verwendet). Aber ich muss definitiv daran arbeiten, herauszufinden, was sie alle tun und wie, wenn ich die Ausgabe lese. = O
keth
6

Was @Joshua für MySQL anzeigt , funktioniert auch in Postgres. BooleanWerte können umgewandelt integerund addiert werden. Die Besetzung muss jedoch eindeutig sein. Für sehr kurzen Code:

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;

Allerdings CASE- obwohl ausführlicher - ist es in der Regel ein kleines bisschen schneller. Und tragbarer, wenn das etwas ausmacht:

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-Geige.

Erwin Brandstetter
quelle
2

Verwenden der Ganzzahldivision und der binären Umwandlung

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
Serpiton
quelle
1

Ich möchte hier nur eine Variation der @Craig Ringers-Version hinterlassen, die ich zufällig gefunden habe. Vielleicht ist sie für jemanden in der Zukunft nützlich.

Anstelle von mehreren UNION ALLs werden unnest und array verwendet. Quelle für Inspiration: /programming/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 Fiddle: http://sqlfiddle.com/#!12/4980b/3

keth
quelle