2010-09-29 21 views
9

He estado jugando con juegos en SQL Server 2000 y tienen la siguiente estructura de la tabla para una de mis tablas temporales (#Periods):Selección SUM del TOP 2 valores dentro de una tabla con GRUPO múltiple en SQL

 
    RestCTR  HoursCTR Duration Rest 
    ---------------------------------------- 
    1   337   2   0 
    2   337   46   1 
    3   337   2   0 
    4   337   46   1 
    5   338   1   0 
    6   338   46   1 
    7   338   2   0 
    8   338   46   1 
    9   338   1   0 
    10   339   46   1 
    ... 

Lo que me gustaría hacer es calcular la suma de los 2 períodos de descanso más largos para cada HoursCTR, preferiblemente utilizando conjuntos y tablas temporales (en lugar de cursores o subconsultas anidadas).

Aquí está la consulta sueño que no va a funcionar en SQL (no importa cuántas veces lo ejecuto):

Select HoursCTR, SUM (TOP 2 Duration) as LongestBreaks 
FROM #Periods 
WHERE Rest = 1 
Group By HoursCTR  

El HoursCTR puede tener cualquier número de períodos de descanso (incluyendo ninguno).

Mi solución actual no es muy elegante y básicamente implica los pasos siguientes:

  1. Obtener la duración máxima de descanso, el grupo de HoursCTR
  2. Seleccione la primera (min) RestCTR fila que devuelve esta duración máxima para cada HoursCTR
  3. Repita el paso 1 (con exclusión de las filas ya recopilados en el paso 2)
  4. Repite el paso 2 (de nuevo, con exclusión de filas recogidos en el paso 2)
  5. Combinar el RestCTR filas (de la etapa 2 y 4) en una sola mesa
  6. Get suma de la duración apuntado por las filas en el paso 5, agrupados por HoursCTR

Si hay algún funciones establecidas que cortan este proceso hacia abajo, se sería muy bienvenido

Respuesta

7

La mejor manera de hacer esto en SQL Server es una common table expression, numeración de las filas en cada grupo con la función de intervalo ROW_NUMBER():

WITH NumberedPeriods AS (
    SELECT HoursCTR, Duration, ROW_NUMBER() 
    OVER (PARTITION BY HoursCTR ORDER BY Duration DESC) AS RN 
    FROM #Periods 
    WHERE Rest = 1 
) 
SELECT HoursCTR, SUM(Duration) AS LongestBreaks 
FROM NumberedPeriods 
WHERE RN <= 2 
GROUP BY HoursCTR 

edición: He añadido una cláusula ORDER BY en la partición, para obtener los dos descansos más largos.


Mea culpa, que no se dio cuenta de que lo que necesita que esto funcione en Microsoft SQL Server 2000. Esa versión no soporta CTE o funciones de ventanas. Dejaré la respuesta anterior en caso de que ayude a alguien más.

En SQL Server 2000, el consejo común es utilizar una consulta correlacionada:

SELECT p1.HoursCTR, (SELECT SUM(t.Duration) FROM 
    (SELECT TOP 2 p2.Duration FROM #Periods AS p2 
    WHERE p2.HoursCTR = p1.HoursCTR 
    ORDER BY p2.Duration DESC) AS t) AS LongestBreaks 
FROM #Periods AS p1 
+0

¿Cómo seleccionar dos descansos _longest_ ¿Qué me falta? – Arkadiy

+2

OP indicado SQL Server 2000. ROW_NUMBER() y CTE no están disponibles. – bobs

+0

@bobs: Gracias, me lo perdí. He agregado una solución diferente. –

1

Por desgracia para ti, Alex, tienes la solución correcta: subconsultas correlacionadas, dependiendo de la forma en que está estructurada , terminará disparando varias veces, lo que podría darte cientos de ejecuciones de consultas individuales.

Ponga su solución actual en el Analizador de consultas, active "Mostrar plan de ejecución" (Ctrl + K) y ejecútelo. Tendrás una pestaña adicional en la parte inferior que te mostrará cómo el motor realizó el proceso de recopilación de resultados. Si haces lo mismo con la subconsulta correlacionada, verás qué hace esa opción.

Creo que es probable que martillee la tabla #Periods tantas veces como filas individuales en esa tabla.

Además, algo me desagrada acerca de la subconsulta correlacionada, me parece a mí. Como los evito como la peste, sabiendo que son malvados, no estoy seguro de cómo arreglarlo.

+0

Gracias David, esperaba que alguien supiera trucos ingeniosos que no había descubierto, pero parece que este proceso de 6 pasos podría ser el final de la línea. –

+0

Bueno ... le está diciendo al motor de base de datos qué hacer y cómo hacerlo, lo cual no es tan malo, realmente. El motor no es tan brillante, cuando se trata de eso. ¿Cómo debe saber que no quiere basura iterativa si le dice que lo haga? –

2

SQL 2000 no tiene CTE ni ROW_NUMBER().
Las subconsultas correlacionadas pueden necesitar un paso adicional al usar group by.

Esto debería funcionar para usted:

SELECT 
    F.HoursCTR, 
    MAX (F.LongestBreaks) AS LongestBreaks -- Dummy max() so that groupby can be used. 
FROM 
    (
     SELECT 
      Pm.HoursCTR, 
      (
       SELECT 
        COALESCE (SUM (S.Duration), 0)  
       FROM 
        (
         SELECT TOP 2 T.Duration 
         FROM   #Periods AS T 
         WHERE   T.HoursCTR = Pm.HoursCTR 
         AND    T.Rest  = 1 
         ORDER BY  T.Duration DESC 
        ) AS S 
      ) AS LongestBreaks 
     FROM 
      #Periods AS Pm 
    ) AS F 
GROUP BY 
    F.HoursCTR 
Cuestiones relacionadas