SQL Server 2008 dan yang lebih baru
Di SQL Server 2008 dan yang lebih baru, tentu saja cara tercepat adalah Convert(date, @date)
. Ini dapat dilemparkan kembali ke datetime
atau datetime2
jika perlu.
Apa yang Benar-Benar Terbaik di SQL Server 2005 dan yang Lebih Lama?
Saya telah melihat klaim yang tidak konsisten tentang apa yang tercepat untuk memotong waktu dari tanggal di SQL Server, dan beberapa orang bahkan mengatakan mereka melakukan pengujian, tetapi pengalaman saya berbeda. Jadi mari kita lakukan pengujian yang lebih ketat dan biarkan semua orang memiliki skripnya sehingga jika saya membuat kesalahan, orang-orang dapat mengoreksi saya.
Konversi Mengambang Tidak Akurat
Pertama, saya akan menghindari konversi datetime
untuk float
, karena tidak mengonversi dengan benar. Anda mungkin lolos dengan melakukan penghapusan waktu secara akurat, tetapi saya pikir itu adalah ide yang buruk untuk menggunakannya karena secara implisit berkomunikasi dengan pengembang bahwa ini adalah operasi yang aman dan tidak . Lihat:
declare @d datetime;
set @d = '2010-09-12 00:00:00.003';
select Convert(datetime, Convert(float, @d));
-- result: 2010-09-12 00:00:00.000 -- oops
Ini bukanlah sesuatu yang harus kita ajarkan kepada orang-orang dalam kode kita atau dalam contoh kita secara online.
Juga, ini bahkan bukan cara tercepat!
Bukti – Pengujian Kinerja
Jika Anda ingin melakukan beberapa pengujian sendiri untuk melihat bagaimana metode yang berbeda benar-benar ditumpuk, maka Anda memerlukan skrip penyiapan ini untuk menjalankan pengujian lebih jauh ke bawah:
create table AllDay (Tm datetime NOT NULL CONSTRAINT PK_AllDay PRIMARY KEY CLUSTERED);
declare @d datetime;
set @d = DateDiff(Day, 0, GetDate());
insert AllDay select @d;
while @@ROWCOUNT != 0
insert AllDay
select * from (
select Tm =
DateAdd(ms, (select Max(DateDiff(ms, @d, Tm)) from AllDay) + 3, Tm)
from AllDay
) X
where Tm < DateAdd(Day, 1, @d);
exec sp_spaceused AllDay; -- 25,920,000 rows
Harap perhatikan bahwa ini membuat tabel 427,57 MB di database Anda dan akan memakan waktu sekitar 15-30 menit untuk dijalankan. Jika basis data Anda kecil dan disetel ke pertumbuhan 10%, ini akan memakan waktu lebih lama daripada jika Anda berukuran cukup besar terlebih dahulu.
Sekarang untuk skrip pengujian kinerja yang sebenarnya. Harap dicatat bahwa ini bertujuan untuk tidak mengembalikan baris ke klien karena ini sangat mahal pada 26 juta baris dan akan menyembunyikan perbedaan kinerja antara metode.
Hasil Kinerja
set statistics time on;
-- (All queries are the same on io: logical reads 54712)
GO
declare
@dd date,
@d datetime,
@di int,
@df float,
@dv varchar(10);
-- Round trip back to datetime
select @d = CONVERT(date, Tm) from AllDay; -- CPU time = 21234 ms, elapsed time = 22301 ms.
select @d = CAST(Tm - 0.50000004 AS int) from AllDay; -- CPU = 23031 ms, elapsed = 24091 ms.
select @d = DATEDIFF(DAY, 0, Tm) from AllDay; -- CPU = 23782 ms, elapsed = 24818 ms.
select @d = FLOOR(CAST(Tm as float)) from AllDay; -- CPU = 36891 ms, elapsed = 38414 ms.
select @d = CONVERT(VARCHAR(8), Tm, 112) from AllDay; -- CPU = 102984 ms, elapsed = 109897 ms.
select @d = CONVERT(CHAR(8), Tm, 112) from AllDay; -- CPU = 103390 ms, elapsed = 108236 ms.
select @d = CONVERT(VARCHAR(10), Tm, 101) from AllDay; -- CPU = 123375 ms, elapsed = 135179 ms.
-- Only to another type but not back
select @dd = Tm from AllDay; -- CPU time = 19891 ms, elapsed time = 20937 ms.
select @di = CAST(Tm - 0.50000004 AS int) from AllDay; -- CPU = 21453 ms, elapsed = 23079 ms.
select @di = DATEDIFF(DAY, 0, Tm) from AllDay; -- CPU = 23218 ms, elapsed = 24700 ms
select @df = FLOOR(CAST(Tm as float)) from AllDay; -- CPU = 29312 ms, elapsed = 31101 ms.
select @dv = CONVERT(VARCHAR(8), Tm, 112) from AllDay; -- CPU = 64016 ms, elapsed = 67815 ms.
select @dv = CONVERT(CHAR(8), Tm, 112) from AllDay; -- CPU = 64297 ms, elapsed = 67987 ms.
select @dv = CONVERT(VARCHAR(10), Tm, 101) from AllDay; -- CPU = 65609 ms, elapsed = 68173 ms.
GO
set statistics time off;
Beberapa Analisis Bertele-tele
Beberapa catatan tentang ini. Pertama-tama, jika hanya melakukan GROUP BY atau perbandingan, tidak perlu mengonversi kembali ke datetime
. Jadi Anda dapat menghemat beberapa CPU dengan menghindarinya, kecuali jika Anda memerlukan nilai akhir untuk tujuan tampilan. Anda bahkan dapat mengelompokkan menurut nilai yang belum dikonversi dan menempatkan konversi hanya dalam klausa SELECT:
select Convert(datetime, DateDiff(dd, 0, Tm))
from (select '2010-09-12 00:00:00.003') X (Tm)
group by DateDiff(dd, 0, Tm)
Juga, lihat bagaimana konversi numerik hanya membutuhkan sedikit lebih banyak waktu untuk mengkonversi kembali ke datetime
, tetapi varchar
konversi hampir dua kali lipat? Ini mengungkapkan bagian CPU yang dikhususkan untuk perhitungan tanggal dalam kueri. Ada bagian dari penggunaan CPU yang tidak melibatkan penghitungan tanggal, dan ini tampaknya mendekati 19875 md dalam kueri di atas. Kemudian konversi tersebut membutuhkan jumlah tambahan, jadi jika ada dua konversi, jumlah tersebut habis kira-kira dua kali.
Pemeriksaan lebih lanjut mengungkapkan bahwa dibandingkan dengan Convert(, 112)
, Convert(, 101)
kueri memiliki beberapa pengeluaran CPU tambahan (karena menggunakan varchar
yang lebih panjang ?), karena konversi kedua kembali ke date
tidak memerlukan biaya sebesar konversi awal ke varchar
, tetapi dengan Convert(, 112)
ini mendekati biaya dasar CPU 20000 md yang sama.
Berikut adalah perhitungan waktu CPU yang saya gunakan untuk analisis di atas:
method round single base
----------- ------ ------ -----
date 21324 19891 18458
int 23031 21453 19875
datediff 23782 23218 22654
float 36891 29312 21733
varchar-112 102984 64016 25048
varchar-101 123375 65609 7843
-
putaran adalah waktu CPU untuk perjalanan pulang pergi ke
datetime
. -
lajang adalah waktu CPU untuk satu konversi ke tipe data alternatif (salah satu yang memiliki efek samping menghapus porsi waktu).
-
dasar adalah perhitungan pengurangan dari
single
perbedaan antara dua pemanggilan:single - (round - single)
. Ini adalah angka rata-rata yang mengasumsikan konversi ke dan dari tipe data itu dandatetime
kira-kira sama di kedua arah. Tampaknya asumsi ini tidak sempurna tetapi mendekati karena nilainya mendekati 20000 md dengan hanya satu pengecualian.
Satu hal lagi yang menarik adalah bahwa biaya dasar hampir sama dengan Convert(date)
single tunggal metode (yang harus hampir 0, karena server secara internal dapat mengekstrak bagian hari bilangan bulat langsung dari empat byte pertama datetime
tipe data).
Kesimpulan
Jadi yang terlihat adalah varchar
satu arah metode konversi membutuhkan waktu sekitar 1,8 dtk dan DateDiff
. satu arah metode ini membutuhkan waktu sekitar 0,18 s. Saya mendasarkan ini pada waktu "CPU dasar" paling konservatif dalam pengujian saya dengan total 18458 md untuk 25.920.000 baris, jadi 23218 md / 25920000 =0,18 dtk. Peningkatan 10x yang nyata tampak seperti banyak, tetapi sejujurnya cukup kecil sampai Anda berurusan dengan ratusan ribu baris (penghematan 617k baris =1 detik).
Bahkan dengan sedikit peningkatan mutlak ini, menurut saya, DateAdd
metode menang karena merupakan kombinasi terbaik dari kinerja dan kejelasan. Jawaban yang membutuhkan "angka ajaib" 0.50000004
akan menggigit seseorang suatu hari nanti (lima nol atau enam???), ditambah lagi lebih sulit untuk dipahami.
Catatan Tambahan
Ketika saya punya waktu, saya akan mengubah 0.50000004
ke '12:00:00.003'
dan lihat bagaimana caranya. Itu dikonversi ke datetime
yang sama nilai dan saya merasa lebih mudah untuk diingat.
Bagi mereka yang tertarik, pengujian di atas dijalankan di server di mana @@Version mengembalikan yang berikut:
Microsoft SQL Server 2008 (RTM) - 10.0.1600.22 (Intel X86) 9 Jul 2008 14:43:34 Hak Cipta (c) 1988-2008 Microsoft Corporation Standard Edition pada Windows NT 5.2 (Build 3790:Service Pack 2)