sql >> Databasteknik >  >> RDS >> Mysql

SQL:Returnerar det vanligaste värdet för varje person

Preliminär kommentar

Lär dig att använda den explicita JOIN-notationen, inte den gamla (före 1992) implicita join-notationen.

Gammal stil:

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

Önskad stil:

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

Du behöver ett PÅ-villkor för varje JOIN.

Även personID värden i datan är strängar, inte siffror, så du måste skriva

 WHERE personTable.personid = "Ben"

till exempel för att få frågan att fungera på tabellerna som visas.

Huvudsvar

Du försöker hitta ett aggregat av ett aggregat:i det här fallet är det maximala antalet. Så, alla generella lösningar kommer att involvera både MAX och COUNT. Du kan inte tillämpa MAX direkt på COUNT, men du kan tillämpa MAX på en kolumn från en underfråga där kolumnen råkar vara ett COUNT.

Bygg upp frågan med testdriven frågedesign — TDQD.

Välj person och transaktionsbetyg

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

Välj person, betyg och antal förekomster av betyg

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

Detta resultat kommer att bli en underfråga.

Hitta det maximala antalet gånger personen får något betyg

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

Nu vet vi vilket som är det maximala antalet för varje person.

Obligatoriskt resultat

För att få resultatet måste vi välja de rader från underfrågan som har det maximala antalet. Observera att om någon har 2 bra och 2 dåliga betyg (och 2 är det maximala antalet betyg av samma typ för den personen), kommer två poster att visas för den personen.

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

Om du vill ha det faktiska antalet betyg också, är det enkelt att välja.

Det är en ganska komplex del av SQL. Jag skulle hata att försöka skriva det från början. Ja, jag skulle nog inte bry mig; Jag skulle utveckla det steg-för-steg, mer eller mindre som visas. Men eftersom vi har felsökt underfrågorna innan vi använder dem i större uttryck, kan vi vara säkra på svaret.

WITH klausul

Observera att Standard SQL tillhandahåller en WITH-sats som prefix till en SELECT-sats och namnger en underfråga. (Det kan också användas för rekursiva frågor, men vi behöver det inte här.)

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

Det här är enklare att skriva. Tyvärr stöder MySQL ännu inte WITH-satsen.

SQL ovan har nu testats mot IBM Informix Dynamic Server 11.70.FC2 som körs på Mac OS X 10.7.4. Det testet avslöjade problemet som diagnostiserades i den preliminära kommentaren. SQL för huvudsvaret fungerade korrekt utan att behöva ändras.



  1. Returnera alla rader med MAX-värdet i SQL?

  2. 3 funktioner som hämtar dagen, månaden och året från ett datum i PostgreSQL

  3. Postgres-fel:Mer än en rad returneras av en underfråga som används som uttryck

  4. PHP bulk infoga foreach