Частина запиту, яка збільшує процесор протягом тривалого періоду, - це функції пункту GROUP BY і той факт, що для цього групування завжди потрібно буде нерозподілений сортування. Хоча індекс у полі часової позначки допоможе початковому фільтру, ця операція повинна бути виконана в кожному рядку, який відповідає фільтру. Прискорення цього використання більш ефективного маршруту, щоб зробити ту саму роботу, як запропонував Алекс, допоможе, але ви все ще маєте величезну неефективність, тому що будь-коли поєднання функцій, яке ви використовуєте, планувальник запитів не зможе придумати щось, що допоможе будь-якому індексу, тому йому доведеться виконувати кожен рядок, спочатку виконуючи функції для обчислення значень групування, лише після цього він може впорядкувати дані та обчислити агрегати за отриманими групуваннями.
Таким чином, рішення полягає в тому, щоб якось зробити групу процесів чимось, для чого вона може використовувати індекс, або інакше усунути необхідність врахувати відразу всі відповідні рядки.
Ви можете підтримувати додатковий стовпець для кожного рядка, що містить час, округлений до години, та індексувати цей стовпець для використання в таких запитах. Це денормалізує ваші дані, щоб вони могли відчувати себе "брудними", але це спрацювало б і було б чистішим, ніж кешування всіх агрегатів для подальшого використання (і оновлення цього кеша в міру зміни базових даних). Додатковий стовпець повинен підтримуватися тригером або бути збереженим обчисленим стовпцем, а не підтримуватися логікою в іншому місці, оскільки це гарантуватиме всі поточні та майбутні місця, які можуть вставити дані або оновити стовпчики часових позначок або існуючі рядки, привести до послідовних даних у новому стовпчик. Ви все ще можете отримати MIN (часову позначку). Отриманий таким чином запит - це все-таки прогулянка по всіх рядках (очевидно, що цього не уникнути), але це може зробити порядок індексування, виведення рядка для кожного групування, коли він отримує наступне значення в індексі, а не запам'ятовувати весь набір рядків для операції нерозбірливого сортування, перш ніж групування / агрегація може бути виконана. Він також буде використовувати набагато менше пам’яті, оскільки йому не потрібно буде запам’ятовувати будь-які рядки з попередніх значень групування, щоб обробити той, який він зараз дивиться, або решту з них.
Цей метод видаляє необхідність знайти десь у пам'яті для всього набору результатів і зробити невпорядкований сортування для групової операції та видаляє обчислення значень групи з великого запиту (переміщення цього завдання на окремі ВСТАНОВКИ / ОНОВЛЕННЯ, які виробляють даних) і має дозволяти таким запитам запускатися прийнятно, не потребуючи підтримки окремого сховища зведених результатів.
Метод, який ніденормалізувати ваші дані, але все ще потребує додаткової структури, це використовувати "таблицю часу", в цьому випадку такий, що містить один рядок на годину за весь час, який ви, ймовірно, вважаєте. Ця таблиця не займе значної кількості місця в БД або помітного розміру - охоплювати часовий проміжок 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 не зробить нічого іншого, крім відкручення процесора трохи більше (якщо планувальник запитів не помітить, що це буде необов'язковим, в цьому випадку він буде ігноруйте його і не використовуйте додатковий час процесора).