Semua jawaban yang ada (berfungsi) memiliki satu dari dua masalah:
- Mereka akan mengabaikan indeks pada kolom yang dicari
- Keinginan (berpotensi) memilih data yang tidak dimaksudkan, secara diam-diam merusak hasil Anda.
1. Indeks yang Diabaikan:
Untuk sebagian besar, ketika kolom yang dicari memiliki fungsi yang dipanggil (termasuk secara implisit, seperti untuk CAST
), pengoptimal harus mengabaikan indeks pada kolom dan menelusuri setiap catatan. Berikut ini contoh singkatnya:
Kita berurusan dengan stempel waktu, dan sebagian besar RDBMS cenderung menyimpan informasi ini sebagai semacam peningkatan nilai, biasanya long
atau BIGINTEGER
hitungan mili-/nanodetik. Waktu saat ini terlihat/disimpan seperti ini:
1402401635000000 -- 2014-06-10 12:00:35.000000 GMT
Anda tidak melihat nilai 'Tahun' ('2014'
) di sana, kan? Faktanya, ada sedikit matematika yang rumit untuk diterjemahkan bolak-balik. Jadi, jika Anda memanggil salah satu fungsi bagian ekstraksi/tanggal pada kolom yang dicari, server harus melakukan semua matematika itu hanya untuk mencari tahu apakah Anda dapat memasukkannya ke dalam hasil. Pada tabel kecil ini bukan masalah, tetapi karena persentase baris yang dipilih berkurang, ini menjadi saluran yang lebih besar dan lebih besar. Kemudian dalam hal ini, Anda melakukannya untuk kedua kalinya untuk menanyakan tentang MONTH
... yah, Anda mendapatkan gambarannya.
2. Data yang tidak diinginkan:
Bergantung pada versi SQL Server tertentu, dan tipe data kolom, menggunakan BETWEEN
(atau rentang batas atas inklusif serupa:<=
) dapat mengakibatkan pemilihan data yang salah. Pada dasarnya, Anda berpotensi memasukkan data dari tengah malam hari "berikutnya", atau mengecualikan beberapa bagian dari catatan hari "saat ini".
Apa yang seharusnya lakukan:
Jadi kami membutuhkan cara yang aman untuk data kami, dan akan menggunakan indeks (jika memungkinkan). Cara yang benar adalah dalam bentuk:
WHERE date_created >= @startOfPreviousMonth AND date_created < @startOfCurrentMonth
Mengingat hanya ada satu bulan, @startOfPreviousMonth
dapat dengan mudah diganti/diturunkan dengan:
DATEADD(month, -1, @startOCurrentfMonth)
Jika Anda perlu mendapatkan awal bulan saat ini di server, Anda dapat melakukannya melalui yang berikut:
DATEADD(month, DATEDIFF(month, 0, CURRENT_TIMESTAMP), 0)
Penjelasan singkat di sini. DATEDIFF(...)
awal akan mendapatkan perbedaan antara awal era saat ini (0001-01-01
- AD, CE, apa pun), pada dasarnya mengembalikan bilangan bulat besar. Ini adalah hitungan bulan ke awal saat ini bulan. Kami kemudian menambahkan nomor ini ke awal era, yaitu pada awal bulan tertentu.
Jadi skrip lengkap Anda bisa/harus terlihat seperti berikut:
DECLARE @startOfCurrentMonth DATETIME
SET @startOfCurrentMonth = DATEADD(month, DATEDIFF(month, 0, CURRENT_TIMESTAMP), 0)
SELECT *
FROM Member
WHERE date_created >= DATEADD(month, -1, @startOfCurrentMonth) -- this was originally misspelled
AND date_created < @startOfCurrentMonth
Dengan demikian, semua operasi tanggal hanya dilakukan sekali, pada satu nilai; pengoptimal bebas menggunakan indeks, dan tidak ada data yang salah yang akan disertakan.