2010-01-04 23 views
6

Tengo una situación complicada al tratar de obtener información de múltiples consultas en una sola fila.¿Cómo combino dos consultas (unión de todo) en una fila?

Considérese la siguiente tabla:

CpuUage: 
    Time time 
    Group char(10) 
    Subsys char(4) 
    Jobs int 
    Cpu  int 

que sostiene los siguientes datos:

Time Group Subsys Jobs Cpu 
----- ------ ------ ---- --- 
00:00 group1 NORM  1 101 (grp1-norm) A1 
01:00 group1 SYS7  3 102 (grp1-sys7) A2 
01:00 group1 NORM  5 104 (grp1-norm) A1 
02:00 group1 NORM  7 108 (grp1-norm) A1 
02:00 group2 NORM  9 116 (grp2-norm) B1 
02:00 group3 SYS7  11 132 (grp3-sys7) C2 
03:00 group1 SYS7  13 164 (grp1-sys7) A2 
03:00 group1 IGNR  99 228 (grp1-ignr) -- 

Los marcadores de la derecha (por ejemplo, A1) son las secciones en el informe a continuación que cada fila se supone afectar.

Necesito una consulta que pueda devolver una sola fila para cada grupo de usuarios, pero con una condición. Los valores para Jobs y Cpu tienen que ir a diferentes columnas dependiendo de la ID del subsistema y solo me interesan los ID de subsistema SYS7 y NORM.

Así, por ejemplo, necesitamos la siguiente (los A/B/1/2 bits son una referencia cruzada de nuevo a las filas anteriores):

  <------ 1 ------> <------ 2 ------> 
    Group NormJobs NormCpu Sys7Jobs Sys7Cpu 
    ------ -------- ------- -------- ------- 
A: group1  13  313  16  266 
B: group2   9  116   0  0 
C: group3   0  0  11  164 

Nuestra solución de informes de edad podría ejecutar varias consultas (con un union all), entonces post-proceso de las filas de combinar los que tienen el mismo nombre de grupo, de modo que:

Group NormJobs NormCpu Sys7Jobs Sys7Cpu 
------ -------- ------- -------- ------- 
group1  13  313   0  0 
group1   0  0  16  266 

se fusionaron juntos, a lo largo de las líneas de:

select groupname, sum(jobs), sum(cpu), 0, 0 from tbl 
    where subsys = 'NORM' 
    group by groupname 
union all 
select groupname, 0, 0, sum(jobs), sum(cpu) from tbl 
    where subsys = 'SYS7' 
    group by groupname 

Lamentablemente, nuestra nueva solución no permite el postprocesamiento y todo tiene que hacerse en la consulta SQL.

Teniendo en cuenta que puede haber grupos con filas SYS7, filas NORM, ambos o ninguno, ¿cuál es la mejor manera de lograr esto?

Pensé en sub-consultar la tabla desde dentro de una selección externa, pero eso puede tener ramificaciones de rendimiento.

Además, eso sería un dolor, ya que tendría que hacer la consulta externa incluyen norma y SYS7 subsistemas continuación, ejecute una subconsulta para cada campo (no puedo realizar la consulta externa sólo para puestos de trabajo desde NORM la presencia de un grupo con solo SYS7 filas no se detectaría de esa manera).

¿Pueden ustedes tejer cualquiera de sus magias izquierda-medio-fuera-fuera de juego para crear una solución viable?

Preferiría una solución independiente del proveedor pero, si debe ir a un proveedor específico, la plataforma es DB2. Aún así, otras plataformas pueden al menos darme una idea de qué probar, así que estoy feliz de verlas.

+0

Si lo hace la prueba y comparar soluciones, estaría interesado en ver Los resultados. Instintivamente, espero que cualquier solución para de alguna forma realizar la tarea de la declaración CASE. Es decir. "Si groupname = g y subsys = s, entonces agregue a aggregate (g, s)". Espero más de una diferencia en términos de recuperación de datos. Sin embargo, estoy abierto a la posibilidad de que un SGBD pueda aprovechar la información del índice para reducir el número de comparaciones necesarias al agregar ciertos grupos. Sugiero que incluya en sus pruebas las permutaciones donde su orden físico de clasificación (índice agrupado) es (subsistemas, nombre de grupo). –

Respuesta

13

No entiendo el problema con la sub-consulta, parece que debería ser igual de rápido:

select 
    sub.gn as groupname, 
    sum(sub.nj) as NormJobs, sum(sun.nc) as NormCpu, 
    sum(sub.sj) as Sys7Jobs, sum(sub.sc) as Sys7Cpu 
    from (
     select 
      groupname as gn, 
      sum(jobs) as nj, sum(cpu) as nc, 
      0 as sj, 0 as sc 
     from tbl 
      where subsys = 'NORM' 
      group by groupname 
     union all select 
      groupname as gn, 
      0 as nj, 0 as nc, 
      sum(jobs) as sj, sum(cpu) as sc 
      from tbl 
      where subsys = 'SYS7' 
      group by groupname 
    ) as sub 
    group by sub.gn 
    order by 1 
+0

Estaba preocupado por tener que hacer una subconsulta para cada grupo en la consulta principal, pero su solución parece haber eludido esto. Parece que solo serían las dos subconsultas independientemente de cuántos grupos haya. Gracias por su aporte, definitivamente voy a dar una oportunidad. – paxdiablo

+0

+1 y aceptar. Este hizo el truco con solo dos golpes en la base de datos y es lo suficientemente rápido para mis necesidades. Sin embargo, lo he editado para que coincida con los estrictos requisitos de DB2 y para modificar un poco la unión. – paxdiablo

4

Es una consulta típica de pivote - aquí es cómo usted lo haría con CASE statements:

SELECT t.group, 
     SUM(CASE 
      WHEN t.subsys = 'NORM' THEN t.jobs 
      ELSE NULL 
     END CASE) AS NormJobs, 
     SUM(CASE 
      WHEN t.subsys = 'NORM' THEN t.cpu 
      ELSE NULL 
     END CASE) AS NormCpu, 
     SUM(CASE 
      WHEN t.subsys = 'SYS7' THEN t.jobs 
      ELSE NULL 
     END CASE) AS Sys7Jobs, 
     SUM(CASE 
      WHEN t.subsys = 'SYS7' THEN t.cpu 
      ELSE NULL 
     END CASE) AS Sys7Cpu 
    FROM CPUUSAGE t 
GROUP BY t.group 

Por desgracia, las instrucciones CASE de DB2 necesitan terminar con END CASE, cuando Oracle/SQL Server/MySQL/PostgreSQL no lo hace. Bueno, PLSQL admite END CASE ...

También está el PIVOT syntax, que también es compatible con Oracle 11g y SQL Server 2005+.

+0

Esta es definitivamente una mejor solución que la mía; No estaba al tanto de este 'pivote', ¡gracias por compartir! –

+0

Su suma debe encapsular todo el CASO porque subsistemas no es parte del GRUPO. Consulte http://stackoverflow.com/questions/1997519/how-do-i-combine-two-queries-union-all-into-one-row/1997581#1997581 –

+0

@Craig: corregido, gracias. –

5

Esta es una consulta de tabla dinámica. (Buscar en que si necesita más información.)

La estructura de consulta que desea es algo a lo largo de las líneas de los siguientes:

SELECT groupname, 
     SUM(CASE WHEN subsys = 'NORM' THEN jobs ELSE 0 END) AS NormJobs, 
     SUM(CASE WHEN subsys = 'NORM' THEN cpu ELSE 0 END) AS NormCpu, 
     SUM(CASE WHEN subsys = 'SYS7' THEN jobs ELSE 0 END) AS Sys7Jobs, 
     SUM(CASE WHEN subsys = 'SYS7' THEN cpu ELSE 0 END) AS Sys7Cpu, 
     SUM(CASE WHEN subsys NOT IN ('NORM', 'SYS7') THEN jobs ELSE 0 END) AS OtherJobs, 
     SUM(CASE WHEN subsys NOT IN ('NORM', 'SYS7') THEN cpu ELSE 0 END) AS OtherCpu 
FROM ??? 
GROUP BY groupname 
+0

+1: Para el formato SUME CASE correcto. –

+0

Al igual que con la respuesta de @ OMG, estoy preocupado por todo lo relacionado con la función por fila. Tendré que verificarlo. No creo que las dos últimas sumas sean necesarias ya que no me importa la información "OtherXxx", solo la NORM y la SYS7. Gracias, tengo que hacer algunas pruebas de rendimiento. – paxdiablo

+0

+1 por ayudarme pero @ la solución de BlueRaja funcionó bien con solo dos golpes en el DB así que no necesité probar las otras soluciones. Me pondré a prueba brevemente porque quiero saber de una vez por todas si estas funciones por fila son realmente lo suficientemente rápidas. Gracias por tu ayuda. – paxdiablo

Cuestiones relacionadas