sql >> Database >  >> RDS >> Mysql

SQL:de meest voorkomende waarde voor elke persoon retourneren

Inleidende opmerking

Leer de expliciete JOIN-notatie te gebruiken, niet de oude (pre-1992) impliciete join-notatie.

Oude stijl:

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

Voorkeursstijl:

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

Je hebt een AAN-voorwaarde nodig voor elke JOIN.

Ook de personID waarden in de data zijn strings, geen getallen, dus je zou moeten schrijven

 WHERE personTable.personid = "Ben"

bijvoorbeeld om de query te laten werken op de getoonde tabellen.

Hoofdantwoord

U zoekt een aggregaat van een aggregaat:in dit geval het maximum van een telling. Elke algemene oplossing omvat dus zowel MAX als COUNT. U kunt MAX niet rechtstreeks toepassen op COUNT, maar u kunt MAX toepassen op een kolom vanuit een subquery waarbij de kolom toevallig een COUNT is.

Bouw de query op met Test-Driven Query Design — TDQD.

Selecteer persoon en transactiebeoordeling

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

Selecteer persoon, beoordeling en aantal keren dat de beoordeling voorkomt

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

Dit resultaat wordt een subquery.

Zoek het maximale aantal keren dat de persoon een beoordeling krijgt

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 weten we wat het maximale aantal is voor elke persoon.

Vereist resultaat

Om het resultaat te krijgen, moeten we de rijen uit de subquery selecteren die het maximale aantal hebben. Houd er rekening mee dat als iemand 2 goede en 2 slechte beoordelingen heeft (en 2 het maximale aantal beoordelingen van hetzelfde type voor die persoon is), er twee records voor die persoon worden weergegeven.

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

Als u ook het werkelijke aantal beoordelingen wilt, is dat eenvoudig te selecteren.

Dat is een vrij complex stukje SQL. Ik zou het vreselijk vinden om te proberen dat helemaal opnieuw te schrijven. Inderdaad, ik zou waarschijnlijk niet de moeite nemen; Ik zou het stap voor stap ontwikkelen, min of meer zoals weergegeven. Maar omdat we de subquery's hebben opgespoord voordat we ze in grotere uitdrukkingen gebruiken, kunnen we zeker zijn van het antwoord.

WITH clausule

Houd er rekening mee dat Standard SQL een WITH-component bevat die voorafgaat aan een SELECT-instructie en een subquery noemt. (Het kan ook worden gebruikt voor recursieve zoekopdrachten, maar dat hebben we hier niet nodig.)

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

Dit is eenvoudiger te schrijven. Helaas ondersteunt MySQL de WITH-clausule nog niet.

De bovenstaande SQL is nu getest tegen IBM Informix Dynamic Server 11.70.FC2 die draait op Mac OS X 10.7.4. Die test bracht het probleem aan het licht dat in de inleidende opmerking werd vastgesteld. De SQL voor het hoofdantwoord werkte correct zonder te hoeven worden gewijzigd.



  1. mysql match tegen ~ voorbeeld

  2. Een MySQL-database maken met behulp van de cPanel API

  3. java.sql.SQLException:Kolomindex buiten bereik, 0 <1

  4. Identiteitstoename in SQL Server 2012-kolom springt van 6 naar 1000+ bij 7e invoer