sql >> Database teknologi >  >> RDS >> PostgreSQL

Få n grupperede kategorier og summer andre til én

Den specifikke sværhedsgrad her:Forespørgsler med en eller flere samlede funktioner i SELECT liste og ingen GROUP BY klausul producere nøjagtig én række, selvom ingen række er fundet i den underliggende tabel.

Der er intet du kan gøre i WHERE klausul for at undertrykke den række. Du skal udelukke sådan en række efter kendsgerningen , dvs. i HAVING klausul eller i en ydre forespørgsel.

Per dokumentation:

Hvis en forespørgsel indeholder samlede funktionskald, men ingen GROUP BY klausul, gruppering forekommer stadig:resultatet er en enkelt grupperække (eller måske i det hele taget norows, hvis den enkelte række derefter elimineres ved HAVING ). Det samme gælder, hvis det indeholder en HAVING klausul, selv uden aggregatfunktionskald eller GROUP BY klausul.

Det skal bemærkes, at tilføjelse af en GROUP BY klausul med kun et konstant udtryk (som ellers er fuldstændig meningsløst!) virker også. Se eksempel nedenfor. Men jeg vil helst ikke bruge det trick, selvom det er kort, billigt og enkelt, for det er næppe indlysende, hvad det gør.

Følgende forespørgsel kræver kun en enkelt tabelscanning og returnerer de 7 bedste kategorier sorteret efter antal. Hvis (og kun hvis ) der er flere kategorier, resten er opsummeret i 'Andre':

WITH cte AS (
   SELECT categoryid, count(*) AS data
        , row_number() OVER (ORDER BY count(*) DESC, categoryid) AS rn
   FROM   contents
   GROUP  BY 1
   )
(  -- parentheses required again
SELECT categoryid, COALESCE(ca.name, 'Unknown') AS label, data
FROM   cte
LEFT   JOIN category ca ON ca.id = cte.categoryid
WHERE  rn <= 7
ORDER  BY rn
)
UNION ALL
SELECT NULL, 'Others', sum(data)
FROM   cte
WHERE  rn > 7         -- only take the rest
HAVING count(*) > 0;  -- only if there actually is a rest
-- or: HAVING  sum(data) > 0
  • Du er nødt til at bryde båndene, hvis flere kategorier kan have det samme antal på tværs af 7./8. rang. I mit eksempel kategorier med det mindre categoryid vinde sådan et løb.

  • Parentes er påkrævet for at inkludere en LIMIT eller ORDER BY klausul til en individuel del af en UNION forespørgsel.

  • Du behøver kun at tilslutte dig tabellen category for de 7 bedste kategorier. Og det er generelt billigere at samle først og slutte sig til senere i dette scenarie. Så deltag ikke i basisforespørgslen i CTE (fælles tabeludtryk) ved navn cte , kun deltage i den første SELECT af UNION forespørgsel, det er billigere.

  • Ikke sikker på, hvorfor du har brug for COALESCE . Hvis du har en fremmednøgle på plads fra contents.categoryid til category.id og begge contents.categoryid og category.name er defineret NOT NULL (som de nok burde være), så behøver du det ikke.

Den ulige GROUP BY true

Dette ville også virke:

...

UNION ALL
SELECT NULL , 'Others', sum(data)
FROM   cte
WHERE  rn > 7
GROUP BY true; 

Og jeg får endda lidt hurtigere forespørgselsplaner. Men det er et ret mærkeligt hack ...

SQL Fiddle demonstrerer alle.

Relateret svar med mere forklaring til UNION ALL / LIMIT teknik:

  • Summer resultaterne af nogle få forespørgsler, og find derefter top 5 i SQL


  1. Hvordan ændrer jeg SQL Server 2005 til at skelne mellem store og små bogstaver?

  2. pgAdmin Alternativer - PostgreSQL Database Management GUI ClusterControl

  3. Understøtter din Salesforce-driver massehandlinger?

  4. Hvordan indlæses en XML-fil i en database ved hjælp af en SSIS-pakke?