A parte da consulta que maximiza a CPU por longos períodos são as funções da cláusula GROUP BY e o fato de que o agrupamento sempre exigirá uma classificação não indexada nessa instância. Embora um índice no campo de carimbo de data e hora ajude o filtro inicial, esta operação deve ser executada em todas as linhas correspondentes ao filtro. Acelerar isso está usando uma rota mais eficiente para fazer o mesmo trabalho sugerido por Alex, mas você ainda tem uma enorme ineficiência, porque qualquer combinação de funções que você usa no planejador de consultas não poderá criar algo que será ajudado por qualquer índice e, portanto, ele precisará percorrer todas as linhas executando primeiro as funções para calcular os valores de agrupamento; somente então ele poderá solicitar os dados e calcular os agregados nos agrupamentos resultantes.
Portanto, a solução é, de alguma forma, tornar o grupo de processos algo que ele possa usar um índice ou remover a necessidade de considerar todas as linhas correspondentes de uma só vez.
Você pode manter uma coluna extra para cada linha que contém o tempo arredondado para a hora e indexar essa coluna para uso em tais consultas. Isso está desnormalizando seus dados para que possa parecer "sujo", mas funcionaria e seria mais limpo do que armazenar em cache todos os agregados para uso futuro (e atualizar esse cache à medida que os dados base forem alterados). A coluna extra deve ser mantida por gatilho ou ser uma coluna computada persistente, em vez de ser mantida pela lógica em outro lugar, pois isso garantirá todos os locais atuais e futuros que podem inserir dados ou atualizar as colunas de carimbo de data / hora ou as linhas existentes resultam em dados consistentes no novo coluna. Você ainda pode obter o MIN (carimbo de data / hora). O que a consulta resultará dessa maneira ainda é um passeio por todas as linhas (isso não pode ser evitado, obviamente), mas é possível indexar por ordem, saída de uma linha para cada agrupamento à medida que chega ao próximo valor no índice, em vez de precisar lembrar o conjunto inteiro de linhas para uma operação de classificação não indexada antes que o agrupamento / agregação possa ser executado. Também usará muito menos memória, pois não será necessário lembrar de nenhuma linha de valores de agrupamento anteriores para processar a que está sendo visualizada agora ou o restante.
Esse método elimina a necessidade de encontrar algum lugar na memória para todo o conjunto de resultados e faz a classificação não indexada para a operação do grupo e remove o cálculo dos valores do grupo da grande consulta (movendo esse trabalho para os INSERTs / UPDATEs individuais que produzem o dados) e deve permitir que essas consultas sejam executadas de maneira aceitável sem a necessidade de manter um armazenamento separado dos resultados agregados.
Um método que nãodesnormalizar seus dados, mas ainda exigir estrutura extra, é usar um "horário", neste caso um contendo uma linha por hora durante todo o tempo que você provavelmente considerar. Essa tabela não consumiria uma quantidade significativa de espaço em um banco de dados ou em um tamanho apreciável - para cobrir um período de tempo de 100 anos, uma tabela contendo uma linha de duas datas (o início e o fim da hora, como '2011-01-01 @ 00: 00: 00.0000 ',' 2011-01-01 @ 00: 00: 59.9997 ', o "9997" é o menor número de milissegundos que um campo DATETIME não arredondará para o próximo segundo), que fazem parte do a chave primária em cluster ocupará ~ 14Mbyte de espaço (8 + 8 bytes por linha * 24 horas / dia * 365,25 dias / ano * 100, mais um pouco para a sobrecarga da estrutura em árvore do índice em cluster, mas essa sobrecarga não 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
Isso significa que o planejador de consultas pode organizar o uso do índice em MyData.TimeStamp. O planejador de consultas deve ser brilhante o suficiente para descobrir que pode percorrer a tabela mansa na etapa com o índice em MyData.TimeStamp, produzindo novamente uma linha por agrupamento e descartando cada conjunto ou linhas à medida que atinge o próximo valor de agrupamento. Não é possível armazenar todas as linhas intermediárias em algum lugar da RAM e executar uma classificação não indexada nelas. É claro que esse método exige que você crie o cronograma e verifique se ele abrange o suficiente para trás e para frente, mas você pode usá-lo para consultas em vários campos de datas em consultas diferentes, onde a opção "coluna extra" exigiria uma coluna computada extra para cada campo de data necessário para filtrar / agrupar dessa maneira e o tamanho pequeno da tabela (a menos que você precise abranger 10,
O método do cronograma possui uma diferença extra (que pode ser bastante vantajosa) em comparação com a situação atual e a solução da coluna computada: ele pode retornar linhas por períodos para os quais não há dados, simplesmente alterando INNER JOIN na consulta de exemplo acima ser ESQUERDO EXTERIOR.
Algumas pessoas sugerem não ter uma tabela de horários física, mas sempre retornando-a de uma função de retorno de tabela. Isso significa que o conteúdo da tabela de horários nunca é armazenado no disco (ou precisa ser lido a partir) e se a função for bem escrita, você nunca precisará se preocupar com o tempo que a tabela de tempo precisa para ir e voltar no tempo, mas eu duvide do custo da CPU de produzir uma tabela na memória para algumas linhas. Toda consulta vale a pequena economia de aborrecimento ao criar (e manter, caso seu período de tempo precise estender além do limite da sua versão inicial) o horário físico.
Uma observação: você também não precisa da cláusula DISTINCT na sua consulta original. O agrupamento garantirá que essas consultas retornem apenas uma linha por período considerado, para que o DISTINCT não faça nada além de girar um pouco mais a CPU (a menos que o planejador de consultas perceba que o distinto seria um não operacional, caso em que ignore-o e não use tempo de CPU extra).