Частью запроса является максимальная загрузка ЦП на длительные периоды - это функции в предложении GROUP BY и тот факт, что группирование всегда будет требовать неиндексированной сортировки в этом случае. Хотя индекс в поле отметки времени поможет начальному фильтру, эта операция должна выполняться для каждой строки, которой соответствует фильтр. Ускорение этого - использование более эффективного маршрута для выполнения той же работы, которая предложена Алексом, поможет, но у вас все еще есть огромная неэффективность, потому что любая комбинация функций, которую вы используете планировщиком запросов, не сможет придумать что-то, что будет помогать любому индексу, так что ему придется проходить через каждую строку, сначала запустив функции для вычисления значений группировки, только затем он сможет упорядочить данные и вычислить агрегаты по результирующим группировкам.
Таким образом, решение состоит в том, чтобы каким-то образом создать группу процессов, для которой она может использовать индекс, или иначе исключить необходимость учитывать все совпадающие строки одновременно.
Вы можете сохранить дополнительный столбец для каждой строки, содержащей время, округленное до часа, и индексировать этот столбец для использования в таких запросах. Это денормализует ваши данные, поэтому может показаться «грязным», но это будет работать и будет чище, чем кэширование всех агрегатов для будущего использования (и обновление этого кэша по мере изменения базовых данных). Дополнительный столбец должен поддерживаться триггером или быть постоянным вычисляемым столбцом, а не поддерживаться логикой в другом месте, поскольку это будет гарантировать все текущие и будущие места, в которые могут быть вставлены данные или обновлены столбцы меток времени или существующие строки приводят к согласованным данным в новом колонка. Вы все еще можете получить МИН (отметку времени). То, что запрос приведет таким образом, - это все-таки обход всех строк (этого, очевидно, избежать нельзя), но он может делать это в порядке индексации, вывод строки для каждой группировки при переходе к следующему значению в индексе, вместо того, чтобы запоминать весь набор строк для неиндексированной операции сортировки, прежде чем можно будет выполнить группирование / агрегирование. Он также будет использовать намного меньше памяти, поскольку ему не нужно будет запоминать строки из предыдущих значений группировки, чтобы обработать то, на что он смотрит сейчас, или остальные.
Этот метод устраняет необходимость поиска где-то в памяти для всего набора результатов и выполнения неиндексированной сортировки для групповой операции и удаляет вычисление значений группы из большого запроса (перемещая это задание в отдельные INSERTs / UPDATE, которые производят данные) и должны позволять таким запросам выполняться приемлемо без необходимости хранить отдельное хранилище агрегированных результатов.
Метод, который неденормализуйте ваши данные, но все же требует дополнительной структуры, это использование «расписания», в данном случае одного, содержащего одну строку в час, на все время, которое вы, вероятно, рассмотрите. Эта таблица не будет занимать значительный объем пространства в БД или значительный размер - для покрытия временного промежутка в 100 лет таблица, содержащая одну строку из двух дат (начало и конец часа, например '2011-01-01 @ 00: 00: 00.0000 ',' 2011-01-01 @ 00: 00: 59.9997 ', "9997" - это наименьшее число миллисекунд, которое поле DATETIME не округляет до следующей секунды), которые являются частью Кластерный первичный ключ будет занимать ~ 14 Мбайт пространства (8 + 8 байт на строку * 24 часа / день * 365,25 дней / год * 100, плюс немного для издержек на структуру дерева кластерного индекса, но эти издержки не будут значительными) ,
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
Это означает, что планировщик запросов может организовать индекс для MyData.TimeStamp, который будет использоваться. Планировщик запросов должен быть достаточно ярким, чтобы понять, что он может шагать по таблице ручного управления в шаге с индексом MyData.TimeStamp, снова выводя по одной строке на группу и отбрасывая каждый набор или строки, когда он достигает следующего значения группировки. Нет необходимости хранить все промежуточные строки где-то в оперативной памяти, а затем выполнять неиндексированную сортировку. Конечно, этот метод требует, чтобы вы создали таблицу времени и удостоверились, что она охватывает достаточно далеко как вперед, так и назад, но вы можете использовать таблицу времени для запросов ко многим полям даты в разных запросах, где в качестве опции «дополнительный столбец» потребуется дополнительный вычисляемый столбец для каждого поля даты, которое необходимо отфильтровать / сгруппировать таким образом, и небольшой размер таблицы (если только он не нужен для охвата 10,
У метода таблицы времени есть дополнительная разница (которая может быть весьма выгодна) по сравнению с текущей ситуацией и решением для вычисляемых столбцов: он может возвращать строки за периоды, для которых нет данных, просто путем изменения INNER JOIN в примере запроса выше быть ЛЕВЫМ НАРУЖНЫМ.
Некоторые люди предлагают не иметь физического расписания, но вместо этого всегда возвращать его из функции возврата таблицы. Это означает, что содержимое таблицы времени никогда не сохраняется (или должно быть прочитано с диска), и если функция хорошо написана, вам никогда не придется беспокоиться о том, как долго таблица времени должна перемещаться назад и вперед во времени, но я сомневайтесь в том, что затраты ЦП на создание таблицы в памяти для некоторых строк каждого запроса стоят небольшой экономии затрат на создание (и поддержание, если его временной интервал должен превысить предел вашей первоначальной версии) физической временной таблицы.
Примечание: вам не нужно это предложение DISTINCT в исходном запросе. Группировка обеспечит, чтобы эти запросы возвращали только одну строку за рассматриваемый период, поэтому DISTINCT ничего не будет делать, только немного увеличит нагрузку на ЦП (если планировщик запросов не заметит, что отличное будет неактивным, в этом случае он будет игнорируйте его и не используйте дополнительное время процессора).