Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

SQL:Retornando o valor mais comum para cada pessoa

Comentário preliminar


Por favor, aprenda a usar a notação JOIN explícita, não a antiga notação de junção implícita (pré-1992).

Estilo antigo:
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

Estilo preferido:
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

Você precisa de uma condição ON para cada JOIN.

Além disso, o personID os valores nos dados são strings, não números, então você precisa escrever
 WHERE personTable.personid = "Ben"

por exemplo, para que a consulta funcione nas tabelas mostradas.

Resposta principal


Você está procurando um agregado de um agregado:neste caso, o máximo de uma contagem. Portanto, qualquer solução geral envolverá MAX e COUNT. Você não pode aplicar MAX diretamente a COUNT, mas pode aplicar MAX a uma coluna de uma subconsulta em que a coluna é uma COUNT.

Construa a consulta usando o Test-Driven Query Design — TDQD.

Selecione a classificação da pessoa e da transação

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

Selecione pessoa, classificação e número de ocorrências de classificação

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

Esse resultado se tornará uma subconsulta.

Encontre o número máximo de vezes que a pessoa recebe qualquer classificação

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

Agora sabemos qual é a contagem máxima para cada pessoa.

Resultado obrigatório


Para obter o resultado, precisamos selecionar as linhas da subconsulta que possuem a contagem máxima. Observe que se alguém tiver 2 avaliações Bom e 2 Ruim (e 2 é o número máximo de avaliações do mesmo tipo para essa pessoa), dois registros serão mostrados para essa pessoa.
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 você também deseja a contagem real de classificação, isso é facilmente selecionado.

Essa é uma parte bastante complexa de SQL. Eu odiaria tentar escrever isso do zero. Na verdade, eu provavelmente não me incomodaria; Eu desenvolveria passo a passo, mais ou menos como mostrado. Mas como depuramos as subconsultas antes de usá-las em expressões maiores, podemos ter certeza da resposta.

COM cláusula


Observe que o SQL padrão fornece uma cláusula WITH que prefixa uma instrução SELECT, nomeando uma subconsulta. (Também pode ser usado para consultas recursivas, mas não precisamos disso aqui.)
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

Isso é mais simples de escrever. Infelizmente, o MySQL ainda não suporta a cláusula WITH.

O SQL acima agora foi testado no IBM Informix Dynamic Server 11.70.FC2 em execução no Mac OS X 10.7.4. Esse teste expôs o problema diagnosticado no comentário preliminar. O SQL para a resposta principal funcionou corretamente sem precisar ser alterado.