2014-09-09 7 views
16

Lets sagen, ich habe eine Tabelle census mit den folgenden Informationen genannt:Insgesamt Zusammenfassung mit mehreren GROUP BY

COUNTRY  PROVINCE CITY  POPULATION 
============================================== 
USA   California Sacramento 1234 
USA   California SanFran  4321 
USA   Texas  Houston  1111 
USA   Texas  Dallas  2222 
Canada  Ontario  Ottawa  3333 
Canada  Manitoba Winnipeg 4444 

Ich baue einen Bericht auf dem Land/der Provinz-Ebene, die mir folgendes ergibt:

SELECT country, province, SUM(population) 
FROM census 
GROUP BY country, province; 

COUNTRY  PROVINCE SUM(POPULATION) 
======================================= 
USA   California 5555 
USA   Texas  3333 
Canada  Ontario  3333 
Canada  Manitoba 4444 

ich suche eine „Gesamtübersicht“ Reihe haben auf den Bericht aufgenommen, so dass das Endergebnis aussieht:

COUNTRY  PROVINCE SUM(POPULATION) 
======================================= 
USA   California 5555 
USA   Texas  3333 
Canada  Ontario  3333 
Canada  Manitoba  4444 
TOTAL     16665 

Ich kenne ROLLUP s, aber ich kann nicht scheinen, eine Kombination zu finden, die mich bekommt, wonach ich suche. Die Verwendung von enthält den Gesamtwert, den ich möchte, aber es enthält auch eine große Anzahl von zusätzlichen Werten, die mir egal sind. Dies gilt auch für GROUP BY ROLLUP(country), province

Wie kann ich den "Gesamt" -Datensatz erstellen?
Ich berechne es gerade mit einem und wiederhole 90% der ersten Abfrage mit einem anderen GROUP BY, aber weil die erste Abfrage nicht trivial ist, ist das Ergebnis langsamer und hässlicher Code.

Hier ist eine SQL-Fiddle für diejenigen, die mit diesem spielen wollen: http://sqlfiddle.com/#!4/12ad9/5

+0

möglich Duplikat [Hinzufügen eines Zusammenfassungszeile mit Summen] (http://stackoverflow.com/questions/17934318/add-a-summary-row-with-totals) – Bulat

+0

@Bulat - Das ist für ein einzelnes Spalte in einer GROUP BY und verwendet SQL Server-spezifische Syntax. –

+0

Nun, Sie können die gleiche Antwort dort finden und geben Sie eine Stimme. Wenn nur die Anzahl der Spalten und ihre Namen wichtig wären ... Und trotzdem ist es ein +1 für die gut präsentierte Frage. – Bulat

Antwort

5

Ok Siehe, ich kam schließlich zwei Ansätze auf, die flexibel sind und mich nicht wie fühlen ein schrecklicher Programmierer.


Die erste Lösung beinhaltet GROUPING SETS.
Was ich im Wesentlichen versuche zu tun ist gruppieren Sie den Ausdruck auf zwei verschiedenen Ebenen: eine auf der gesamten Ebene und eine auf der Ebene (country, province).

Wenn ich die Abfrage in zwei Teile aufteilen und eine verwenden würde, hätte die eine Hälfte eine GROUP BY country, province und die andere würde eine Gruppierungsklausel fehlen. Der nicht gruppierte Abschnitt kann auch als GROUP BY() dargestellt werden, wenn wir uns danach fühlen. Dies wird in einem Moment nützlich sein.

Das führt uns so etwas wie gibt:

SELECT country, province, SUM(population) 
FROM census 
GROUP BY country, province 
UNION ALL 
SELECT NULL AS country, NULL AS province, SUM(population) 
FROM census 
GROUP BY(); 

Die Abfrage funktioniert, aber es skaliert nicht gut. Je mehr Berechnungen Sie durchführen müssen, desto mehr Zeit verbringen Sie damit, sich zu wiederholen.

Durch eine GROUPING SETS verwendet wird, kann ich angeben, dass ich die Daten auf zwei verschiedene Arten gruppiert werden sollen:

SELECT country, province, SUM(population) 
FROM census 
GROUP BY GROUPING SETS((country, province),()); 

Jetzt kommen wir weiter! Aber was ist mit unserer Ergebnisreihe? Wie können wir es erkennen und entsprechend beschriften? Hier kommt die GROUPING-Funktion ins Spiel. Sie gibt eine 1 zurück, wenn die Spalte wegen einer GROUP BY-Anweisung NULL ist.

SELECT 
    CASE 
     WHEN GROUPING(country) = 1 THEN 'TOTAL' 
     ELSE country 
    END AS country, 
    province, 
    SUM(population), 
    GROUPING(country) AS grouping_flg 
FROM census 
GROUP BY GROUPING SETS ((country, province),()); 

Wenn wir den GROUPING SETS Ansatz nicht gefällt, können wir immer noch ein traditionelles ROLLUP verwenden, aber mit einer geringfügigen Änderung.

Anstatt jede Spalte einzeln an die ROLLUP zu übergeben, übergeben wir die Auflistung von Spalten als eine Gruppe, indem Sie sie in Klammern einschließen. Dies macht es so, dass der Satz von Spalten als eine einzelne Gruppe anstelle von mehrere Gruppen behandelt wird. Die folgende Abfrage gibt Ihnen die gleichen Ergebnisse wie die vorherige:

SELECT 
    CASE 
     WHEN GROUPING(country) = 1 THEN 'TOTAL' 
     ELSE country 
    END AS country, 
    province, 
    SUM(population), 
    GROUPING(country) AS grouping_flg 
FROM census 
GROUP BY ROLLUP((country, province)); 

Fühlen Sie sich frei, beide Ansätze für sich selbst ausprobieren!
http://sqlfiddle.com/#!4/12ad9/102

-1

Sie haben eine Union nutzen könnten:

SELECT country, province, SUM(population) 
FROM census 
GROUP BY country, province 
UNION 
SELECT 
    'Total', '', SUM(population) 
FROM census 
+0

Warum wurde das abgelehnt? – evanv

+2

Das ursprüngliche Plakat sagte, dass sie bereits eine UNION machten und es war ineffizient und "hässlich". Die SQL-Geige zeigte dies im Detail. Sie wollten vermeiden, eine Gewerkschaft zu machen. – Anssssss

2

Das erste, was herauszufiltern ist, die Teilsummen in den Sinn kommt nachdem die rollup angewendet:

SELECT * 
FROM (SELECT country, province, SUM (population) 
     FROM  census 
     GROUP BY ROLLUP (country, province)) 
WHERE province IS NOT NULL OR country IS NULL; 

Sie können ein köstliches ish die gleiche Sache ein wenig kompakter durch GROUPING_ID in der HAVING-Klausel:

SELECT country, 
     province, 
     SUM (population) 
FROM  census 
GROUP BY ROLLUP (country, province) 
HAVING GROUPING_ID (country, province) <> 1 

Und wie @Anssssss wies darauf hin, auch die Kriterien von der WHERE Klausel in der ersten Antwort in ein verwenden können HAVING Klausel:

SELECT country, province, SUM (population) 
FROM  census 
GROUP BY ROLLUP (country, province) 
HAVING province IS NOT NULL OR country IS NULL 
-1

Ive kommen mit einer sQL-Union bis zum Ende Ihrer Ergebnisse hinzufügen mit Gesamt. Sie können die query here

SELECT country, province, SUM(population) as population, 0 as OrderBy 
FROM census 
GROUP BY country, province 
UNION 
SELECT country, province, population, 1 as OrderBy FROM (
    SELECT 'Total' as country, '' as province, SUM(population) as population 
    FROM census 
) 
ORDER BY OrderBy; 
3

In Oracle sehen Sie dies mit einer having Klausel tun können:

SELECT coalesce(c.country, 'Total') as province, c.country, SUM(c.population) 
FROM census c 
GROUP BY ROLLUP(c.country, c.province) 
HAVING c.province is not null or 
     c.province is null and c.country is null; 

Here die SQL Fiddle ist.

11

Das ist genau das, was GROUPING SETS Ausdrücke zu tun wurde entwickelt:

SELECT country, province, SUM(population) 
FROM census 
GROUP BY GROUPING SETS 
    ((country, province),  -- first group by country and province 
    ()       -- then by (nothing), i.e. a total grouping 
    ); 

die SQL-Fiddle

+0

Cool! Wie erkenne ich nun die Zusammenfassungszeile mit der Funktion "GROUPING"? Ich kann es scheinbar nicht weitergeben, ohne dass es einen Fehler gibt. Ich habe auch herausgefunden, dass 'ROLLUP ((Land, Provinz)) 'auch funktioniert, aber Sie haben mich auf die Antwort geschlagen. : D –

+0

Ignoriere meinen vorherigen Kommentar. 'GROUPING' gibt 1 zurück, wenn die Spalte wegen eines ROLLUPs null ist, also funktionieren' GROUPING (country) 'und' GROUPING (province) 'beide. –

+0

Übrigens (in Bezug auf Ihre gelöschte Antwort) bedeutet "GROUP BY 1" normalerweise etwas anderes. 'GROUP BY()' ist eigentlich gültiges SQL für eine Gesamtgruppe von, das entspricht keiner Gruppenklausel. –