Saya memiliki tabel SQL Server dengan lebih dari 3 miliar baris. Salah satu permintaan saya membutuhkan waktu yang sangat lama sehingga saya mempertimbangkan untuk mengoptimalkannya. Kueri terlihat seperti ini:
SELECT [Enroll_Date]
,Count(*) AS [Record #]
,Count(Distinct UserID) AS [User #]
FROM UserTable
GROUP BY [Enroll_Date]
[Enroll_Date] adalah kolom selektivitas rendah dengan kurang dari 50 nilai yang mungkin, sedangkan kolom UserID adalah kolom selektivitas tinggi dengan lebih dari 200 juta nilai berbeda. Berdasarkan penelitian saya, saya percaya saya harus membuat indeks komposit non-cluster pada dua kolom ini, dan secara teori kolom selektivitas tinggi harus menjadi kolom pertama. Tetapi saya tidak yakin dalam kasus saya, apakah itu akan berhasil karena saya menggunakan kolom selektivitas rendah dalam grup dengan klausa.
Tabel ini tidak memiliki indeks berkerumun.
sumber
Jawaban:
Sebagai alternatif untuk solusi @ AaronBertrand (jika Anda tidak dapat atau tidak ingin membuat tampilan yang diindeks), saya akan merekomendasikan Anda untuk membuat indeks
(Enroll_Date, UserID)
. Jika jenis pertanyaan ini sangat umum di meja Anda, ini mungkin seharusnya menjadi indeks cluster Anda.Saya biasanya tidak akan merekomendasikan indeks selektivitas tinggi sebagai "praktik terbaik" umum, tetapi melihat indeks apa yang akan memberikan kinerja terbaik pada permintaan Anda.
Indeks aktif
(Enroll_Date, UserID)
akan memberikan kueri Anda rencana kueri yang sangat dioptimalkan, tanpa pemblokiran dengan Agregat Stream."Non-blocking" dalam konteks ini berarti bahwa kueri tidak perlu buffer sejumlah data yang signifikan (seperti, misalnya, semacam atau agregat hash akan), yang berarti itu (a) mulai mengembalikan baris segera, dan ( b) hampir tidak menggunakan memori yang bekerja.
sumber
Jawaban Aarons adalah solusi yang bagus. Saya akan menjawab pertanyaan dengan asumsi Anda tidak ingin mengambil pendekatan itu.
Kueri yang Anda poskan biasanya akan dieksekusi dengan pengelompokan pertama aktif
(Enroll_Date, UserID)
, lalu lagi pada(Enroll_Date)
. Optimasi ini baru untuk SQL Server 2012. Ini berlaku jika ada satuCOUNT DISTINCT
.Indeks pada dua kolom dalam urutan tertentu
(Enroll_Date, UserID)
akan cukup untuk mendapatkan rencana efisien yang menggerakkan pemindaian indeks ke dua Agregat Stream berturut-turut. Urutan sebaliknya tidak akan memungkinkan rencana itu.Karena itu, gunakan perintah
(Enroll_Date, UserID)
. Anda tidak punya pilihan di sini.sumber
Kedengarannya seperti skenario ideal untuk tampilan yang diindeks, yang memungkinkan Anda membayar untuk perhitungan dan agregat pada waktu penulisan, bukan waktu permintaan.
Itu akan membutuhkan waktu untuk dibuat, dan tentu saja akan membutuhkan pemeliharaan di seluruh operasi DML, seperti halnya indeks pada tabel dasar.
Sekarang kueri terhadap tampilan ini akan sangat mirip - setiap baris dalam tampilan sekarang mewakili kombo pengguna / tanggal yang berbeda, sehingga angka tersebut dapat dihitung dengan satu COUNT (*), sedangkan jumlah total baris dalam tabel dasar adalah sudah sebagian dikumpulkan untuk Anda, sekarang Anda hanya perlu menambahkannya menggunakan SUM per tanggal:
Menambahkan petunjuk NOEXPAND, setelah mengingat ini dan ini .
Saya dapat memberi tahu Anda tanpa keraguan bahwa kueri ini akan lebih cepat daripada kueri Anda saat ini (tetapi tidak seberapa banyak), kecuali dalam kasus langka di mana Anda memiliki tepat satu pengguna untuk setiap tanggal (dalam hal ini jumlah data yang sama akan memiliki untuk dibaca) dan kolom yang kita ketahui adalah satu-satunya kolom dalam indeks tabel dasar. Apakah peningkatan kinerja pada waktu baca sepadan dengan kerja ekstra yang akan memengaruhi porsi penulisan dari beban kerja Anda adalah sesuatu yang tidak dapat kami beritahukan kepada Anda - Anda harus mengujinya untuk mengukur trade-off (tidak ada indeks gratis).
Dan jika Anda sering menggunakan klausa WHERE umum yang sama terhadap Enroll_Date untuk rentang yang spesifik dan terdefinisi dengan baik (katakanlah, kuartal saat ini atau tahun ini), Anda bisa menambahkan indeks yang cocok yang disaring yang mengurangi I / O lebih jauh (tapi selalu ada trade-off).
Anda mungkin juga mempertimbangkan untuk meletakkan indeks berkerumun di tabel dasar. Ini sepertinya bukan salah satu dari kasus penggunaan yang sangat jarang yang diuntungkan oleh tumpukan.
sumber