2012-05-10 16 views
7

Estoy tratando de refinar una consulta SQL para que mis informes se vean mejor. Mi consulta lee los datos de una tabla, agrupa por unas pocas columnas y calcula algunos campos agregados (recuentos y sumas).Cómo generar resultados agregados "vacíos" en SQL

SELECT A, B, C, COUNT(*), SUM(D) FROM T 
GROUP BY A, B, C 
ORDER BY A, B, C 

Ahora, vamos a suponer columnas B y C son algunas cadenas constantes definidas, por ejemplo, B puede ser 'B1' o 'B2', C puede ser 'C1' o 'C2'. Por lo tanto, un ejemplo de resultados es:

A | B | C | COUNT(*) | SUM(D) 
-------------------------------- 
A1 | B1 | C1 |  34 | 1752 
A1 | B1 | C2 |  4 | 183 
A1 | B2 | C1 |  199 | 8926 
A1 | B2 | C2 |  56 | 2511 
A2 | B1 | C2 |  6 |  89 
A2 | B2 | C2 |  12 | 231 
A3 | B1 | C1 |  89 | 552 
... 

Como se puede ver, por 'A1' tengo todas las combinaciones posibles de cuatro (B, C), pero eso no es cierto para 'A2'. Mi pregunta es: ¿cómo puedo generar también filas de resumen para la combinación (B, C) que no están presentes, de hecho, en la tabla dada? Es decir, ¿cómo puedo imprimir, por ejemplo, también estas filas:

A | B | C | COUNT(*) | SUM(D) 
-------------------------------- 
A2 | B1 | C1 |  0 |  0 
A2 | B2 | C1 |  0 |  0 

La única solución que puedo ver es crear algunas tablas auxiliarity con todos (B, C) los valores y luego hacer un RIGHT OUTER JOIN con esa mesa auxiliar. Pero estoy buscando una manera más limpia ...

Gracias a todos.

+0

¿Qué estás usando mssql, oracel, mysql? – Arion

+0

Oracle. Pero la consulta está escrita, de momento, de manera estándar (también se ejecuta en MySQL, tenemos una copia MySQL de la base de datos sin motivo relevante para la pregunta). –

+0

Tal vez [esto] (http://www.techrepublic.com/article/group-by-grouping-sets-for-custom-rollups-in-oracle/6134424) ayude. Los conjuntos de agrupamiento están disponibles en SQL Server y Oracle. No estoy seguro acerca de MySQL. –

Respuesta

2

La tabla auxiliar no tiene que ser una tabla real, puede ser una expresión de tabla común, al menos si puede obtener todos los valores posibles (o todo lo que le interese) de la tabla. Usando consulta @Bob Jarvis para generar todas las combinaciones posibles que puede hacer algo como:

WITH CTE AS (
    SELECT * FROM (SELECT DISTINCT a FROM T) 
    JOIN (SELECT DISTINCT b, c FROM T) ON (1 = 1) 
) 
SELECT CTE.A, CTE.B, CTE.C, 
    SUM(CASE WHEN T.A IS NULL THEN 0 ELSE 1 END), NVL(SUM(T.D),0) 
FROM CTE 
LEFT JOIN T ON T.A = CTE.A AND T.B = CTE.B AND T.C = CTE.C 
GROUP BY CTE.A, CTE.B, CTE.C 
ORDER BY CTE.A, CTE.B, CTE.C; 

Si tienen valores fijos que no pueden estar en la tabla a continuación, que es un poco más complicado (o más feo de todos modos, y empeora con los valores más posibles):

WITH CTE AS (
    SELECT * FROM (SELECT DISTINCT a FROM T) 
    JOIN (SELECT 'B1' AS B FROM DUAL 
     UNION ALL SELECT 'B2' FROM DUAL) ON (1 = 1) 
    JOIN (SELECT 'C1' AS C FROM DUAL 
     UNION ALL SELECT 'C2' FROM DUAL) ON (1 = 1) 
) 
SELECT CTE.A, CTE.B, CTE.C, 
    SUM(CASE WHEN T.A IS NULL THEN 0 ELSE 1 END), NVL(SUM(T.D),0) 
FROM CTE 
LEFT JOIN T ON T.A = CTE.A AND T.B = CTE.B AND T.C = CTE.C 
GROUP BY CTE.A, CTE.B, CTE.C 
ORDER BY CTE.A, CTE.B, CTE.C; 

Pero usted tiene que unirse a algo que sabe acerca de los valores que faltan ''. Si se necesita la misma lógica en otro lugar, y tiene valores fijos, entonces una tabla permanente puede ser más limpia; puede necesitarse el mantenimiento en cualquier caso, por supuesto. También podría considerar una función canalizada para actuar como una tabla sustituta, pero tal vez dependa de los volúmenes.

+1

La tabla en sí contiene todas las posibilidades. Obviamente, NO esperaba que el motor de DB los "adivine". +1 por el uso interesante de 'WITH', nunca he visto t antes. –

1

La cuestión es que, si no tiene una combinación particular en su base de datos, ¿cómo sabría un motor incluir esa combinación en los resultados? Para tener todas las combinaciones en los resultados, debe tener todas las combinaciones disponibles, ya sea en la tabla principal o en otra tabla utilizada para hacer referencia. Por ejemplo, puede crear otra tabla R con los datos de este modo:

A | B | C 
------------ 
A1 | B1 | C1 
A1 | B1 | C2 
A1 | B2 | C1 
A1 | B2 | C2 
A2 | B1 | C1 
A2 | B1 | C2 
A2 | B2 | C1 
A2 | B2 | C2 
A3 | B1 | C1 
A3 | B1 | C2 
A3 | B1 | C1 
A3 | B2 | C2 
... 

Y a continuación, la consulta se vería así:

SELECT r.*, COUNT(t.d), coalesce(SUM(t.d), 0) 
FROM r LEFT OUTER JOIN t on (r.a=t.a and r.b=t.b and r.c=t.c) 
GROUP BY r.a, r.b, r.c 
ORDER BY r.a, r.b, r.c 

Esto le va a devolver el conjunto como desee con 0 | 0 para la combinación que no existen en la tabla principal. Tenga en cuenta que esto solo es posible si conoce todas las combinaciones posibles que desea incluir, lo que puede no ser siempre el caso.

Si por el contrario el A, B, C son valores numéricos y lo que desea incluir todos los números en un rango, entonces no puede haber otra forma de hacer frente a esto, algo como esto:

SELECT a.n, b.n, c.n, COUNT(t.d), coalesce(SUM(t.d), 0) 
FROM (SELECT (rownum) "n" FROM DUAL WHERE LEVEL >= start_a CONNECT BY LEVEL <= end_a) a, 
    (SELECT (rownum) "n" FROM DUAL WHERE LEVEL >= start_b CONNECT BY LEVEL <= end_b) b, 
    (SELECT (rownum) "n" FROM DUAL WHERE LEVEL >= start_c CONNECT BY LEVEL <= end_c) c, 
    t 
WHERE a.n = t.a(+) AND b.n = t.b(+) AND c.n = t.c(+) 
GROUP BY a.n, b.n, c.n 
ORDER BY a.n, b.n, c.n 

(No tengo una instancia de Oracle útil para probar esto, así que esto es más una suposición educada en lugar de otra cosa.)

La conclusión es que el motor necesita saber qué incluir en la versión final resultados - de una forma u otra.

+0

Así es como lo estoy haciendo ahora. Ver el último párrafo en mi pregunta. –

+0

@ lorenzo-s El problema principal es que sin que usted especifique toda combinación válida, el motor no tiene forma de saberlo, por lo tanto, necesita tener esta tabla temporal. –

+0

La tabla en sí (considerando todas las filas) contiene todas las combinaciones de valores (B, C). –

0

Hay maneras probablemente más bonitas de hacer esto, pero el siguiente debe empezar hacia lo que quiere:

SELECT * FROM 
    (SELECT DISTINCT a FROM T) 
JOIN 
    (SELECT DISTINCT b, c FROM T) 
    ON (1 = 1) 
ORDER BY a, b, c 

Esto le dará todas las combinaciones que existen de B y C, junto con todos los A, que existe , similar a

A1 B1 C1 
A1 B1 C2 
A1 B2 C1 
A1 B2 C2 
A2 B1 C1 
A2 B1 C2 
A2 B2 C1 
A2 B2 C2 

Comparta y disfrute.

+0

Estoy buscando algo (y no estoy suponiendo que exixts!) Que no implique una unión más o menos ... Actualmente estoy usando una tabla como la que escribió para lograr el resultado. Ver el último párrafo en mi pregunta. –

+0

El problema aquí es diferente. Si la tabla T contiene todas las combinaciones posibles, entonces estás bien, pero no es así, entonces vuelves al punto uno. De alguna manera, el motor de DB necesita saber cuáles son todas las combinaciones posibles. –

+0

@AleksG Ok, vale, no tenemos este problema: la tabla en sí (considerando todas las filas) contiene todas las combinaciones de valores (B, C). Obviamente, NO esperaba que el motor de DB los "adivine". –

Cuestiones relacionadas