online learn how data create books and aggregating sql oracle group-by rollup grouping-sets

sql - learn - sequel database



Resumen general con mĂșltiples GRUPO POR (6)

En Oracle puedes hacer esto con una cláusula have:

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 está el violín de SQL.

Digamos que tengo una tabla llamada census con la siguiente información:

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

Estoy elaborando un informe a nivel de país / provincia, que me da lo siguiente:

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

Estoy buscando tener una fila de "resumen general" incluida en el informe, para que el resultado final se vea como:

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

ROLLUP los ROLLUP , pero parece que no puedo encontrar una combinación que me ROLLUP lo que estoy buscando. El uso de GROUP BY ROLLUP(country, province) incluye el valor total que quiero, pero también incluye una gran cantidad de valores adicionales que no me importan. Esto también es cierto con GROUP BY ROLLUP(country), province

¿Cómo puedo hacer para hacer el registro "total"?
Actualmente lo estoy calculando con UNION ALL y repito el 90% de la primera consulta con un GROUP BY diferente, pero como la primera consulta no es trivial, el resultado es un código lento y feo.

Aquí hay un Fiddle de SQL para aquellos que quieren jugar con esto: http://sqlfiddle.com/#!4/12ad9/5


Esto es exactamente para lo que se diseñaron las expresiones GROUPING SETS :

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 );

Ver el SQL-Fiddle


He creado un sql usando Union para agregar Total al final de sus resultados. Puedes ver la consulta aquí

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;


Lo primero que viene a la mente es filtrar los subtotales después de aplicar el rollup :

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

Puede lograr lo mismo de forma más compacta utilizando GROUPING_ID en la cláusula HAVING :

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

Y, como señaló @Anssssss, también puede utilizar los criterios de la cláusula WHERE en la primera respuesta en una cláusula HAVING :

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


Ok, finalmente encontré dos enfoques que son flexibles y no me hacen sentir como un programador terrible.

La primera solución consiste en GROUPING SETS .
Lo que esencialmente estoy tratando de hacer es agrupar la expresión en dos niveles diferentes: uno a nivel general y otro a nivel (country, province) .

Si tuviera que dividir la consulta en dos partes y utilizar un UNION ALL , una mitad tendría un GROUP BY country, province y el otro carecería de una cláusula de agrupación. La sección no agrupada también se puede representar como GROUP BY () si lo deseamos. Esto será útil en un momento.

Eso nos da algo como:

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 ();

La consulta funciona, pero no se escala bien. Cuantos más cálculos necesite realizar, más tiempo pasará repitiéndose.

Al usar GROUPING SETS , puedo especificar que quiero que los datos se agrupen de dos maneras diferentes:

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

¡Ahora estamos llegando a alguna parte! Pero ¿qué pasa con nuestra fila de resultados? ¿Cómo podemos detectarlo y etiquetarlo en consecuencia? Ahí es donde entra la función GROUPING . Devuelve un 1 si la columna es NULA debido a una instrucción GROUP BY.

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), () );

Si no nos gusta el enfoque de ROLLUP aún podemos usar un ROLLUP tradicional pero con un cambio menor.

En lugar de pasar cada columna al ROLLUP individualmente, pasamos la colección de columnas como un conjunto encerrándolas entre paréntesis. Esto hace que el conjunto de columnas se trate como un solo grupo en lugar de varios grupos. La siguiente consulta le dará los mismos resultados que la anterior:

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) );

¡Siéntete libre de probar ambos enfoques por ti mismo!
http://sqlfiddle.com/#!4/12ad9/102


Podrías hacer uso de una unión:

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