Mysql
 sql >> Database >  >> RDS >> Mysql

SQL:restituisce il valore più comune per ogni persona

Commento preliminare

Impara a usare la notazione esplicita JOIN, non la vecchia notazione di join implicita (precedente al 1992).

Vecchio stile:

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

Stile preferito:

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

Hai bisogno di una condizione ON per ogni JOIN.

Inoltre, il personID i valori nei dati sono stringhe, non numeri, quindi dovresti scrivere

 WHERE personTable.personid = "Ben"

ad esempio, per far funzionare la query sulle tabelle mostrate.

Risposta principale

Stai cercando di trovare un aggregato di un aggregato:in questo caso, il massimo di un conteggio. Quindi, qualsiasi soluzione generale coinvolgerà sia MAX che COUNT. Non puoi applicare MAX direttamente a COUNT, ma puoi applicare MAX a una colonna da una sottoquery in cui la colonna è COUNT.

Crea la query utilizzando Test-Driven Query Design — TDQD.

Seleziona persona e valutazione transazione

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

Seleziona persona, valutazione e numero di occorrenze di valutazione

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

Questo risultato diventerà una sottoquery.

Trova il numero massimo di volte in cui la persona ottiene una valutazione

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

Ora sappiamo qual è il conteggio massimo per ogni persona.

Risultato richiesto

Per ottenere il risultato, dobbiamo selezionare le righe della sottoquery che hanno il conteggio massimo. Tieni presente che se qualcuno ha 2 valutazioni buone e 2 cattive (e 2 è il numero massimo di valutazioni dello stesso tipo per quella persona), verranno mostrati due record per quella persona.

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

Se desideri anche il conteggio effettivo delle valutazioni, puoi selezionarlo facilmente.

Questo è un pezzo di SQL abbastanza complesso. Mi dispiacerebbe provare a scriverlo da zero. In effetti, probabilmente non mi preoccuperei; Lo svilupperei passo dopo passo, più o meno come mostrato. Ma poiché abbiamo eseguito il debug delle sottoquery prima di usarle in espressioni più grandi, possiamo essere certi della risposta.

CON clausola

Si noti che Standard SQL fornisce una clausola WITH che antepone un'istruzione SELECT, denominando una sottoquery. (Può anche essere usato per query ricorsive, ma non ne abbiamo bisogno qui.)

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

Questo è più semplice da scrivere. Sfortunatamente, MySQL non supporta ancora la clausola WITH.

L'SQL sopra è stato ora testato rispetto a IBM Informix Dynamic Server 11.70.FC2 in esecuzione su Mac OS X 10.7.4. Quel test ha esposto il problema diagnosticato nel commento preliminare. L'SQL per la risposta principale ha funzionato correttamente senza bisogno di essere modificato.