Conta dove due o più colonne di una riga superano un determinato valore [basket, double double, triple double]


20

Gioco a una partita di basket che consente di visualizzare le sue statistiche come file di database, in modo da poter calcolare statistiche che non sono implementate nel gioco. Finora non ho avuto alcun problema nel confermare le statistiche che volevo, ma ora ho riscontrato un problema: contare il numero di doppie doppie e / o triple che un giocatore ha guadagnato durante la stagione dalle sue statistiche di gioco.

La definizione di una doppia doppia e una doppia tripla è la seguente:

Doppia-doppia:

Una doppia doppia è definita come una prestazione in cui un giocatore accumula un totale di due cifre in due delle cinque categorie statistiche (punti, rimbalzi, assist, ruba e tiri bloccati) in una partita.

Tripla-doppia:

Una tripla doppia è definita come una prestazione in cui un giocatore accumula un totale di numeri a due cifre in tre delle cinque categorie statistiche (punti, rimbalzi, assist, ruba e tiri bloccati) in una partita.

Quadrupla doppia (aggiunta per chiarimenti)

Una doppia quadrupla è definita come una prestazione in cui un giocatore accumula un totale di due cifre in quattro delle cinque categorie statistiche (punti, rimbalzi, assist, ruba e tiri bloccati) in una partita.

La tabella "PlayerGameStats" memorizza le statistiche per ogni gioco a cui un giocatore gioca e ha il seguente aspetto:

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

L'output che voglio ottenere è simile al seguente:

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

L'unica soluzione che ho trovato finora è così terribile che mi fa vomitare ...; o) ... Sembra così:

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

... e ora probabilmente stai anche vomitando (o ridendo forte) dopo aver letto questo. Non ho nemmeno scritto tutto ciò che sarebbe necessario per ottenere tutte le doppie doppie combinazioni, e ho omesso l'istruzione case per le triple doppie perché è ancora più ridicola.

C'è un modo migliore per farlo? O con la struttura della tabella che ho o con una nuova struttura della tabella (potrei scrivere uno script per convertire la tabella).

Posso usare MySQL 5.5 o PostgreSQL 9.2.

Ecco un link a SqlFiddle con i dati di esempio e la mia terribile soluzione che ho pubblicato sopra: http://sqlfiddle.com/#!2/af6101/3

Nota che non mi interessa molto il doppio quadruplo (vedi sopra) poiché non si verificano nel gioco a cui gioco, per quanto ne so, ma sarebbe un vantaggio se la query fosse facilmente espandibile senza riscrivere molto sull'account per quadruple doppie.

Risposte:


10

Non so se questo è il modo migliore. Per prima cosa ho fatto una selezione per scoprire se uno stat è a doppia cifra e assegnarlo a 1 se lo è. Riassunti tutti quelli per scoprire il numero totale di doppie cifre per partita. Da lì basta riassumere tutte le doppie e le triple. Sembra funzionare

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

Ciao, grazie per la tua soluzione. Mi piace veramente. Fa esattamente quello che voglio ed è facilmente estensibile per includere Quadruple-double e Quintuple-double senza molta scrittura. Farà di questo la risposta accettata per ora. :)
keth,

Mi piace il tuo codice, ma puoi hackerarlo per essere ancora più breve. Non è necessario utilizzare le CASEistruzioni poiché le espressioni booleane valutano 1 se vero e 0 quando falso. L'ho aggiunto alla mia risposta di seguito con un messaggio in quanto non è possibile pubblicare un blocco di codice SQL completo nei commenti qui.
Joshua Huber,

Grazie Giosuè. Totalmente trascurato e sembra molto meglio.
SQLChao,

1
@JoshuaHuber Esatto ma la query funzionerà solo in MySQL. Utilizzando CASEe SUM/COUNTconsente di lavorare anche su Postgres.
ypercubeᵀᴹ

@ypercube: In realtà, aggiungere booleane funziona anche in Postgres. Devi solo lanciare esplicitamente. Ma in CASEgenere è un po 'più veloce. Ho aggiunto una demo con alcuni altri miglioramenti minori.
Erwin Brandstetter,

7

Prova questo (ha funzionato per me su 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

O ancora più breve, strappando clamorosamente il codice di JChao dalla sua risposta, ma togliendo le CASEdichiarazioni non necessarie poiché l'espressione booleana valuta {1,0} quando {Vero, Falso}:

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

Sulla base dei commenti secondo cui il codice sopra riportato non verrà eseguito in PostgreSQL poiché non gli piace fare booleano + booleano. Non mi piace ancora CASE. Ecco una via d'uscita su PostgreSQL (9.3), lanciando a 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, buon punto e grazie. Avevo appena chiesto quell'esatto chiarimento come commento alla domanda sopra. Semantica. Credo che quattro gol nell'hockey siano ancora considerati "tirando una tripletta", ma quattro colpi consecutivi nel bowling potrebbero non essere considerati un "tacchino", piuttosto un "quad". Non sono un esperto di semantica di ogni gioco. Prendi la decisione e scegli =o >=come adatto.
Joshua Huber,

Grazie per la tua soluzione Sicuramente fa quello che voglio. Come anche la versione abbreviata di JChao che hai fornito.
keth,

1
L'aggiunta di booleani non funzionerà in PostgreSQL, tienilo a mente.
Craig Ringer,

@CraigRinger - grazie per averlo sottolineato. Dato che sono ancora verde dietro le orecchie quando si tratta di SQL in generale e PostgreSQl in particolare, questa è per me una preziosa informazione. :)
keth,

1
@CraigRinger Nice, ma non credo che MySQL supporti CAST(... AS int) ( stackoverflow.com/questions/12126991/… ). MySQL può fare CAST(... AS UNSIGNED), il che funziona in questa query, ma PostgreSQL non può. Non sono sicuro che ci sia un comune CASTche entrambi possono fare per la portabilità. Peggior CASE, potrebbe essere bloccato CASEalla fine se la portabilità è fondamentale.
Joshua Huber,

6

Ecco un'altra versione del problema.

Per come la penso, essenzialmente stai lavorando con dati pivot per il problema attuale, quindi la prima cosa da fare è di non ruotarlo. Sfortunatamente PostgreSQL non fornisce strumenti utili per farlo, quindi senza entrare nella generazione dinamica di SQL in PL / PgSQL, possiamo almeno fare:

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

Questo mette i dati in una forma più malleabile, anche se non è certo carino. Qui presumo che (player_id, seasonday) sia sufficiente per identificare in modo univoco i giocatori, ovvero l'ID giocatore è unico tra le squadre. In caso contrario, dovrai includere abbastanza altre informazioni per fornire una chiave univoca.

Con quei dati non pivot ora è possibile filtrarli e aggregarli in modi utili, come:

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;

Questo è tutt'altro che carino, e probabilmente non è così veloce. Tuttavia, è gestibile e richiede modifiche minime per gestire nuovi tipi di statistiche, nuove colonne, ecc.

Quindi è più un "ehi, hai pensato" che un suggerimento serio. L'obiettivo era modellare l'SQL in modo che corrispondesse all'affermazione del problema il più direttamente possibile, piuttosto che renderlo veloce.


Ciò è stato reso molto più semplice dall'uso di inserti multivalore sani e quotazioni ANSI nel tuo SQL orientato a MySQL. Grazie; è bello non vedere i bastoncini per una volta. Tutto quello che dovevo cambiare era la generazione di chiavi sintetiche.


Questo è una specie di cosa avevo in mente.
Colin 't Hart,

1
Grazie per aver pubblicato questa soluzione. Ho avuto i miei problemi nell'implementare qualcosa del genere come suggerito sopra @ Colin'tHart (mai fatto qualcosa del genere prima, ma sembra davvero utile per alcune altre statistiche che potrei voler definire in futuro). È interessante quanti modi ci sono per realizzare il risultato desiderato. Sicuramente ho imparato molto oggi.
keth,

1
Per saperne di più, explain analyzei piani di query (o l'equivalente di MySQL) e capire cosa fanno tutti e come :)
Craig Ringer

@CraigRinger - Grazie. Buon Consiglio. In realtà l'ho fatto con tutte le soluzioni fornite fino ad ora (ho usato SqlFiddles "visualizza il piano di esecuzione"). Ma ho sicuramente bisogno di lavorare sulla parte "capire cosa fanno tutti e come" durante la lettura dell'output. = O
keth,

6

Ciò che @Joshua mostra per MySQL funziona anche in Postgres. Booleani valori possono essere espressi integere aggiunti. Il cast deve essere esplicito, però. Rende il codice molto breve:

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;

Tuttavia, CASEanche se più dettagliato, è in genere un po 'più veloce. E più portatile, se ciò dovesse importare:

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



2

Usando la divisione intera e il cast binario

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

Voglio solo lasciare una variante della versione di @Craig Ringers qui che ho trovato per caso, forse è utile per qualcuno in futuro.

Invece di più UNION ALL, utilizza un array e unest. Fonte per l'ispirazione: /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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.