web-dev-qa-db-fra.com

Comptez lorsque deux ou plusieurs colonnes d'affilée sont sur une certaine valeur [Basketball, Double double, Triple Double]

Je joue un jeu de basketball qui permet de générer ses statistiques en tant que fichier de base de données. On peut donc calculer des statistiques de celle-ci qui ne sont pas implémentées dans le jeu. Jusqu'à présent, je n'ai eu aucun problème à calumrer les statistiques que je voulais, mais maintenant j'ai rencontré un problème: Compter le nombre de doubles doubles et/ou tripler double un joueur effectué au cours de la saison de ses statistiques de jeu.

La définition d'un double double et d'un triple double est la suivante:

double-double-double:

Un double double est défini comme une performance dans laquelle un joueur accumule un nombre à deux chiffres au total dans deux des cinq catégories statistiques-points-points, rebondit, aide, vole et bloqué des tirs - dans une partie.

Triple-Double:

Un triple-double est défini comme une performance dans laquelle un joueur accumule un nombre à deux chiffres au total dans trois des cinq catégories statistiques-points-points, rebondit, aide, vole et bloqué des tirs-dans une partie.

Quadruple-Double (ajouté pour clarification)

Un quadruple-double est défini comme une performance dans laquelle un joueur accumule un nombre à deux chiffres au total dans quatre des cinq catégories statistiques-points-points, rebondit, aide, vole et bloqué des tirs-dans une partie.

La table "PlayergameStats" marque des statistiques pour chaque jeu qu'un joueur joue et regarde comme suit:

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

La sortie que je veux atteindre ressemble à ceci:

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

La seule solution que j'ai trouvée jusqu'à présent est si terrible ça me fait chuter ...; O) ... ça ressemble à ceci:

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

... Et maintenant, vous êtes probablement aussi puking (ou rire dur) après avoir lu ceci. Je n'ai même pas écrit tout ce qui serait nécessaire pour obtenir toutes les doubles combinaisons doubles et omis l'énoncé de cas pour le triplement double, car il est encore plus ridicule.

Y a-t-il une meilleure manière de faire cela? Soit avec la structure de la table que j'ai ou avec une nouvelle structure de table (je pourrais écrire un script pour convertir la table).

Je peux utiliser MySQL 5.5 ou PostgreSQL 9.2.

Voici un lien vers Sqlfiddle avec des données d'exemple et ma terrible solution I Publiée ci-dessus: http://sqlfiddle.com/#!2/af6101/ ==

Notez que je ne suis pas vraiment intéressé par Quadruple-Doubles (voir ci-dessus) car ils ne se produisent pas dans le jeu que je joue aussi loin que je sache, mais ce serait un plus si la requête est facilement extensible sans beaucoup de réécriture sans réécriture. pour quadruple-doubles.

20
keth

Je ne sais pas si c'est le meilleur moyen. J'ai d'abord sélectionné une sélection de choix pour savoir si une statistique est à deux chiffres et l'attribuez-la un 1 si elle est. Résumé tous ceux-ci pour découvrir le nombre total de deux chiffres par match. De là, résumez tous les doubles et triples. Semble fonctionner

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

Essayez ceci (travaillé pour moi sur 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

Ou même plus court, par blatanly déchirant le code de Jchao de sa réponse, mais en supprimant les déclarations CASE depuis que Boolean Expr évalue à {1,0} quand {vrai, faux}:

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

Basé sur les commentaires que le code ci-dessus ne fonctionnera pas dans PostgreSQL depuis que je n'aime pas faire Boolean + Boolean. Je n'aime toujours pas CASE. Voici une solution sur PostgreSQL (9.3), en casting à 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

Voici une autre prise sur le problème.

La façon dont j'en pense, vous travaillez essentiellement avec des données pivotées pour le problème actuel. La première chose à faire est de l'improcher. Malheureusement, PostgreSQL ne fournit pas de beaux outils à faire cela, donc sans entrer dans la génération de SQL dynamique dans PL/PGSQL, nous pouvons au moins faire:

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

Cela met les données sur une forme plus malléable, bien que cela ne soit certainement pas joli. Ici, je suppose que (player_id, Seasonday) est suffisant pour identifier de manière unique les joueurs, c'est-à-dire que l'ID du joueur est unique entre les équipes. Si ce n'est pas le cas, vous devrez inclure suffisamment d'autres informations pour fournir une clé unique.

Avec cette donnée non pivotée, il est maintenant possible de filtrer et de les agréger de manière utile, comme:

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;

C'est loin d'être jolie, et ce n'est probablement pas si vite. Il est maintenu cependant, nécessitant des changements minimes pour gérer de nouveaux types de statistiques, de nouvelles colonnes, etc.

C'est donc plus un "hé, avez-vous pensé" qu'une suggestion sérieuse. L'objectif était de modéliser le SQL à correspondre à la déclaration de problème aussi directement que possible, plutôt que de le rendre rapide.


Ceci a été considérablement plus facile de répondre à votre utilisation d'inserts multi-valorisés sains et d'ANSI citant dans votre SQL orienté MySQL. Merci; C'est bien de ne pas voir des backtecks ​​une fois. Tout ce que je devais changer était la génération de clé synthétique.

6
Craig Ringer

Quoi @ Joshua affiche pour MySQL , fonctionne également à Postgres. Boolean _ valeurs peuvent être distribuées sur integer et ajouté. La distribution doit être explicite, cependant. Donne un code très court:

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;

Cependant, CASE - Même si plus verbose - est généralement un peu plus rapide. Et plus portable, si cela devrait importer:

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.

6

Utiliser une division entière et une distribution binaire

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