Mysql
 sql >> Base de données >  >> RDS >> Mysql

SQL :Renvoyer la valeur la plus courante pour chaque personne

Commentaire préliminaire

Veuillez apprendre à utiliser la notation JOIN explicite, et non l'ancienne notation de jointure implicite (avant 1992).

Style ancien :

SELECT transactionTable.rating as MostCommonRating 
FROM personTable, transactionTable 
WHERE personTable.transactionid = transactionTable.transactionid 
AND personTable.personid = 1
GROUP BY transactionTable.rating 
ORDER BY COUNT(transactionTable.rating) desc 
LIMIT 1

Style préféré :

SELECT transactionTable.rating AS MostCommonRating 
  FROM personTable
  JOIN transactionTable 
    ON personTable.transactionid = transactionTable.transactionid 
 WHERE personTable.personid = 1
 GROUP BY transactionTable.rating 
 ORDER BY COUNT(transactionTable.rating) desc 
 LIMIT 1

Vous avez besoin d'une condition ON pour chaque JOIN.

En outre, le personID les valeurs dans les données sont des chaînes, pas des nombres, vous devez donc écrire

 WHERE personTable.personid = "Ben"

par exemple, pour que la requête fonctionne sur les tables affichées.

Réponse principale

Vous cherchez à trouver un agrégat d'un agrégat :dans ce cas, le maximum d'un décompte. Ainsi, toute solution générale impliquera à la fois MAX et COUNT. Vous ne pouvez pas appliquer MAX directement à COUNT, mais vous pouvez appliquer MAX à une colonne à partir d'une sous-requête où la colonne se trouve être un COUNT.

Créez la requête à l'aide de Test-Driven Query Design (TDQD).

Sélectionnez la note de la personne et de la transaction

SELECT p.PersonID, t.Rating, t.TransactionID
  FROM PersonTable AS p
  JOIN TransactionTable AS t
    ON p.TransactionID = t.TransactionID

Sélectionnez la personne, l'évaluation et le nombre d'occurrences d'évaluation

SELECT p.PersonID, t.Rating, COUNT(*) AS RatingCount
  FROM PersonTable AS p
  JOIN TransactionTable AS t
    ON p.TransactionID = t.TransactionID
 GROUP BY p.PersonID, t.Rating

Ce résultat deviendra une sous-requête.

Trouvez le nombre maximum de fois où la personne obtient une note

SELECT s.PersonID, MAX(s.RatingCount)
  FROM (SELECT p.PersonID, t.Rating, COUNT(*) AS RatingCount
          FROM PersonTable AS p
          JOIN TransactionTable AS t
            ON p.TransactionID = t.TransactionID
         GROUP BY p.PersonID, t.Rating
       ) AS s
 GROUP BY s.PersonID

Nous savons maintenant quel est le nombre maximum pour chaque personne.

Résultat requis

Pour obtenir le résultat, nous devons sélectionner les lignes de la sous-requête qui ont le nombre maximum. Notez que si quelqu'un a 2 bonnes et 2 mauvaises notes (et 2 est le nombre maximum de notes du même type pour cette personne), alors deux enregistrements seront affichés pour cette personne.

SELECT s.PersonID, s.Rating
  FROM (SELECT p.PersonID, t.Rating, COUNT(*) AS RatingCount
          FROM PersonTable AS p
          JOIN TransactionTable AS t
            ON p.TransactionID = t.TransactionID
         GROUP BY p.PersonID, t.Rating
       ) AS s
  JOIN (SELECT s.PersonID, MAX(s.RatingCount) AS MaxRatingCount
          FROM (SELECT p.PersonID, t.Rating, COUNT(*) AS RatingCount
                  FROM PersonTable AS p
                  JOIN TransactionTable AS t
                    ON p.TransactionID = t.TransactionID
                 GROUP BY p.PersonID, t.Rating
               ) AS s
         GROUP BY s.PersonID
       ) AS m
    ON s.PersonID = m.PersonID AND s.RatingCount = m.MaxRatingCount

Si vous voulez que la note réelle compte aussi, c'est facile à sélectionner.

C'est un morceau de SQL assez complexe. Je détesterais essayer d'écrire cela à partir de zéro. En effet, je ne m'en soucierais probablement pas; Je le développerais étape par étape, plus ou moins comme indiqué. Mais comme nous avons débogué les sous-requêtes avant de les utiliser dans des expressions plus grandes, nous pouvons être sûrs de la réponse.

Clause AVEC

Notez que le SQL standard fournit une clause WITH qui préfixe une instruction SELECT, en nommant une sous-requête. (Il peut également être utilisé pour les requêtes récursives, mais nous n'en avons pas besoin ici.)

WITH RatingList AS
     (SELECT p.PersonID, t.Rating, COUNT(*) AS RatingCount
        FROM PersonTable AS p
        JOIN TransactionTable AS t
          ON p.TransactionID = t.TransactionID
       GROUP BY p.PersonID, t.Rating
     )
SELECT s.PersonID, s.Rating
  FROM RatingList AS s
  JOIN (SELECT s.PersonID, MAX(s.RatingCount) AS MaxRatingCount
          FROM RatingList AS s
         GROUP BY s.PersonID
       ) AS m
    ON s.PersonID = m.PersonID AND s.RatingCount = m.MaxRatingCount

C'est plus simple à écrire. Malheureusement, MySQL ne prend pas encore en charge la clause WITH.

Le SQL ci-dessus a maintenant été testé sur IBM Informix Dynamic Server 11.70.FC2 exécuté sur Mac OS X 10.7.4. Ce test a révélé le problème diagnostiqué dans le commentaire préliminaire. Le SQL pour la réponse principale a fonctionné correctement sans avoir besoin d'être modifié.