Bagian dari kueri adalah memaksimalkan CPU untuk jangka waktu yang lama adalah fungsi-fungsi dalam klausa GROUP BY dan fakta bahwa pengelompokan akan selalu membutuhkan pengurutan yang tidak terindeks dalam hal ini. Sementara indeks pada bidang cap waktu akan membantu filter awal operasi ini harus dilakukan pada setiap baris yang cocok dengan filter. Mempercepat ini menggunakan rute yang lebih efisien untuk melakukan pekerjaan yang sama seperti yang disarankan oleh Alex akan membantu, tetapi Anda masih memiliki inefisiensi besar di sana karena kombinasi fungsi apa pun yang Anda gunakan perencana kueri tidak akan dapat muncul dengan sesuatu yang akan dibantu oleh indeks apa pun sehingga harus dijalankan melalui setiap baris terlebih dahulu menjalankan fungsi untuk menghitung nilai pengelompokan, hanya kemudian dapat memesan data dan menghitung agregat atas pengelompokan yang dihasilkan.
Jadi solusinya adalah entah bagaimana membuat grup proses dengan sesuatu yang dapat digunakan indeks, atau menghapus kebutuhan untuk mempertimbangkan semua baris yang cocok sekaligus.
Anda bisa mempertahankan kolom tambahan untuk setiap baris yang berisi waktu dibulatkan ke jam, dan indeks kolom ini untuk digunakan dalam permintaan tersebut. Ini menormalkan data Anda sehingga mungkin terasa "kotor" tetapi itu akan berhasil dan akan lebih bersih daripada melakukan cache semua agregat untuk penggunaan di masa mendatang (dan memperbarui cache itu saat data dasar diubah). Kolom tambahan harus dikelola oleh pemicu atau kolom yang dikomputasi tetap, daripada dikelola oleh logika di tempat lain, karena ini akan menjamin semua tempat saat ini dan masa depan yang mungkin memasukkan data atau memperbarui kolom timestamp atau baris yang ada menghasilkan data yang konsisten di baru kolom. Anda masih bisa mengeluarkan MIN (cap waktu). Apa permintaan akan menghasilkan dengan cara ini masih berjalan di semua baris (ini tidak dapat dihindari, jelas) tetapi dapat melakukannya dengan urutan indeks, mengeluarkan sebuah baris untuk setiap pengelompokan saat mencapai nilai berikutnya dalam indeks daripada harus mengingat seluruh rangkaian baris untuk operasi pengurutan yang tidak indeks sebelum pengelompokan / agregasi dapat dilakukan. Ini akan menggunakan memori jauh lebih sedikit juga, karena tidak perlu mengingat baris dari nilai pengelompokan sebelumnya untuk memproses yang sedang dilihatnya atau sisanya.
Metode itu menghilangkan kebutuhan menemukan di suatu tempat di memori untuk seluruh hasil yang ditetapkan dan melakukan pengurutan tidak terindeks untuk operasi grup dan menghapus perhitungan nilai grup dari kueri besar (memindahkan pekerjaan itu ke individu INSERT / PEMBARUAN yang menghasilkan data) dan harus memungkinkan pertanyaan seperti itu berjalan dengan dapat diterima tanpa perlu mempertahankan penyimpanan terpisah dari hasil agregat.
Metode yang tidakMenormalkan data Anda, tetapi masih membutuhkan struktur tambahan, adalah dengan menggunakan "tabel waktu", dalam hal ini yang berisi satu baris per jam untuk semua waktu yang mungkin Anda pertimbangkan. Tabel ini tidak akan mengkonsumsi ruang yang signifikan dalam DB atau ukuran yang cukup besar - untuk mencakup rentang waktu 100 tahun, tabel yang berisi satu baris dari dua tanggal (awal dan akhir jam, seperti '2011-01-01 @ 00: 00: 00.0000 ',' 2011-01-01 @ 00: 00: 59.9997 ', "9997" adalah jumlah milidetik terkecil, bidang DATETIME tidak akan membulatkan ke detik berikutnya) yang merupakan bagian dari kunci primer yang dikelompokkan akan memakan ruang ~ 14Mbyte (8 + 8 byte per baris * 24 jam / hari * 365,25 hari / tahun * 100, ditambah sedikit untuk overhead struktur pohon indeks berkerumun tetapi overhead itu tidak akan signifikan) .
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
Ini berarti bahwa perencana permintaan dapat mengatur indeks pada MyData.TimeStamp untuk digunakan. Perencana kueri harus cukup cerdas untuk bekerja sehingga dapat berjalan di meja jinak sesuai dengan indeks pada MyData.TimeStamp, lagi-lagi mengeluarkan satu baris per pengelompokan dan membuang setiap set atau baris saat menyentuh nilai pengelompokan berikutnya. Tidak menyimpan semua baris perantara di suatu tempat di RAM kemudian melakukan semacam pengindeksan pada mereka. Tentu saja metode ini mengharuskan Anda membuat tabel waktu dan memastikan rentang waktunya cukup jauh ke belakang dan ke depan, tetapi Anda dapat menggunakan tabel waktu untuk kueri terhadap banyak bidang tanggal dalam kueri yang berbeda, di mana sebagai opsi "kolom tambahan" akan membutuhkan kolom tambahan yang dihitung untuk setiap bidang tanggal yang Anda perlukan untuk memfilter / mengelompokkan dengan cara ini, dan ukuran tabel yang kecil (kecuali jika Anda memerlukannya untuk rentang 10,
Metode tabel waktu memiliki perbedaan ekstra (yang bisa sangat menguntungkan) dibandingkan dengan situasi Anda saat ini dan solusi kolom yang dihitung: metode ini dapat mengembalikan baris untuk periode di mana tidak ada data, cukup dengan mengubah INNER JOIN dalam contoh permintaan di atas menjadi LEFT OUTER.
Beberapa orang menyarankan tidak memiliki tabel waktu fisik, tetapi selalu mengembalikannya dari fungsi pengembalian tabel. Ini berarti isi dari tabel waktu tidak pernah disimpan di (atau perlu dibaca dari) disk dan jika fungsi ditulis dengan baik Anda tidak perlu khawatir tentang berapa lama tabel waktu perlu bolak-balik dalam waktu, tapi saya meragukan biaya CPU dalam menghasilkan tabel di-memori untuk beberapa baris setiap query bernilai penghematan kecil dari kerumitan menciptakan (dan mempertahankan, jika rentang waktu perlu melampaui batas versi awal Anda) tabel waktu fisik.
Catatan tambahan: Anda tidak perlu klausa DISTINCT pada kueri asli Anda. Pengelompokan akan memastikan bahwa kueri ini hanya mengembalikan satu baris per periode yang sedang dipertimbangkan sehingga DISTINCT tidak akan melakukan apa pun selain memutar CPU sedikit lebih banyak (kecuali perencana kueri memperhatikan bahwa perbedaan tersebut akan menjadi no-op dalam hal ini akan abaikan saja dan jangan gunakan waktu CPU tambahan).