La parte de la consulta es maximizar la CPU durante largos períodos son las funciones en la cláusula GROUP BY y el hecho de que la agrupación siempre requerirá una ordenación no indexada en esta instancia. Si bien un índice en el campo de marca de tiempo ayudará al filtro inicial, esta operación debe realizarse en cada fila que coincida con el filtro. Acelerar esto es usar una ruta más eficiente para hacer el mismo trabajo sugerido por Alex ayudará, pero aún tiene una gran ineficiencia allí porque cualquier combinación de funciones que use el planificador de consultas no podrá llegar a algo que será ayudado por cualquier índice, por lo que tendrá que ejecutar cada fila ejecutando primero las funciones para calcular los valores de agrupación, solo entonces puede ordenar los datos y calcular los agregados sobre las agrupaciones resultantes.
Entonces, la solución es de alguna manera hacer que el proceso se agrupe por algo para lo que pueda usar un índice, o de lo contrario eliminar la necesidad de considerar todas las filas coincidentes a la vez.
Puede mantener una columna adicional para cada fila que contenga el tiempo redondeado a la hora e indexar esta columna para usarla en dichas consultas. Esto está denormalizando sus datos, por lo que podría sentirse "sucio", pero funcionaría y sería más limpio que almacenar en caché todos los agregados para su uso futuro (y actualizar esa caché a medida que se modifican los datos base). La columna adicional debe mantenerse por disparador o ser una columna calculada persistente, en lugar de mantenerse por lógica en otro lugar, ya que esto garantizará que todos los lugares actuales y futuros que puedan insertar datos o actualizar las columnas de marca de tiempo o las filas existentes den como resultado datos consistentes en el nuevo columna. Aún puede obtener el MIN (marca de tiempo). Lo que dará como resultado la consulta de esta manera sigue siendo un recorrido por todas las filas (esto no se puede evitar, obviamente), pero puede hacerlo por orden de índice, generar una fila para cada agrupación a medida que llega al siguiente valor en el índice en lugar de tener que recordar todo el conjunto de filas para una operación de clasificación no indexada antes de que se pueda realizar la agrupación / agregación. También usará mucha menos memoria, ya que no necesitará recordar ninguna fila de valores de agrupación anteriores para procesar la que está viendo ahora o el resto de ellas.
Ese método elimina la necesidad de encontrar algún lugar en la memoria para todo el conjunto de resultados y realiza la ordenación no indexada para la operación del grupo y elimina el cálculo de los valores del grupo fuera de la consulta grande (mover ese trabajo a los INSERTOS / ACTUALIZACIONES individuales que producen el datos) y debería permitir que dichas consultas se ejecuten de manera aceptable sin necesidad de mantener un almacén separado de los resultados agregados.
Un método que nodesnormalizar sus datos, pero aún requiere una estructura adicional, es usar una "tabla de tiempos", en este caso una que contenga una fila por hora durante todo el tiempo que probablemente tenga en cuenta. Esta tabla no consumiría una cantidad significativa de espacio en una base de datos o un tamaño apreciable: para cubrir un intervalo de tiempo de 100 años, una tabla que contiene una fila de dos fechas (el inicio y el final de la hora, como '2011-01-01 @ 00: 00: 00.0000 ',' 2011-01-01 @ 00: 00: 59.9997 ', siendo el "9997" el menor número de milisegundos que un campo DATETIME no se redondeará al siguiente segundo), que son parte del la clave primaria en clúster ocupará ~ 14Mbyte de espacio (8 + 8 bytes por fila * 24 horas / día * 365.25 días / año * 100, más un poco para la sobrecarga de la estructura de árbol del índice agrupado, pero esa sobrecarga no será significativa) .
SELECT CONVERT(VARCHAR, [timestamp], 1)+' '+ CAST(DATEPART(Hh,[timestamp]) as VARCHAR) AS TimeStampHour
, MIN([timestamp]) as TimeStamp
, AVG(MyField) As AvgField
FROM TimeRangeByHours tt
INNER JOIN MyData md ON md.TimeStamp BETWEEN tt.StartTime AND tt.EndTime
WHERE tt.StartTime > '4/10/2011'
GROUP BY tt.StartTime
ORDER BY tt.StartTime
Esto significa que el planificador de consultas puede organizar el uso del índice en MyData.TimeStamp. El planificador de consultas debe ser lo suficientemente brillante como para poder descifrar la tabla de domesticación en el paso con el índice en MyData.TimeStamp, generando nuevamente una fila por agrupación y descartando cada conjunto o filas cuando alcanza el siguiente valor de agrupación. No almacenar todas las filas intermedias en algún lugar de la RAM y luego realizar una ordenación no indexada en ellas. Por supuesto, este método requiere que cree la tabla de tiempo y asegúrese de que se extienda lo suficiente tanto hacia atrás como hacia adelante, pero puede usar la tabla de tiempo para consultas en muchos campos de fecha en consultas diferentes, donde la opción "columna adicional" requeriría una columna calculada adicional para cada campo de fecha que necesitaba filtrar / agrupar de esta manera, y el tamaño pequeño de la tabla (a menos que lo necesite para abarcar 10,
El método de la tabla de tiempos tiene una diferencia adicional (que podría ser bastante ventajosa) en comparación con su situación actual y la solución de columna calculada: puede devolver filas para períodos para los que no hay datos, simplemente cambiando INNER JOIN en la consulta de ejemplo anterior ser una IZQUIERDA EXTERIOR.
Algunas personas sugieren no tener un horario físico, sino que siempre lo devuelven desde una función de retorno de la tabla. Esto significa que el contenido de la tabla de tiempos nunca se almacena (o necesita leerse) en el disco y si la función está bien escrita, nunca tendrá que preocuparse por cuánto tiempo la tabla de tiempos debe extenderse de un lado a otro, pero yo dudar del costo de CPU de producir una tabla en memoria para algunas filas, cada consulta vale la pena ahorrar un poco de la molestia de crear (y mantener, si su intervalo de tiempo necesita extenderse más allá del límite de su versión inicial) la tabla de tiempo física.
Una nota al margen: tampoco necesita esa cláusula DISTINCT en su consulta original. La agrupación asegurará que estas consultas solo devuelvan una fila por período en consideración, por lo que DISTINCT no hará nada más que girar la CPU un poco más (a menos que el planificador de consultas advierta que la distinción sería un no-op en cuyo caso ignórelo y no use tiempo de CPU adicional).