Jawaban:
Karena Anda menggunakan datetimedatatype, Anda perlu memahami bagaimana sql server melakukan putaran data datetime.
╔═══════════╦═════╦═════════════════════════════╦═════════════════════════════╦══════════╦═══════════╗
║ Name ║ sn ║ Minimum value ║ Maximum value ║ Accuracy ║ Storage ║
╠═══════════╬═════╬═════════════════════════════╬═════════════════════════════╬══════════╬═══════════╣
║ datetime ║ dt ║ 1753-01-01 00:00:00.000 ║ 9999-12-31 23:59:59.997 ║ 3.33 ms ║ 8 bytes ║
║ datetime2 ║ dt2 ║ 0001-01-01 00:00:00.0000000 ║ 9999-12-31 23:59:59.9999999 ║ 100ns ║ 6-8 bytes ║
╚═══════════╩═════╩═════════════════════════════╩═════════════════════════════╩══════════╩═══════════╝
Menggunakan kueri di bawah ini, Anda dapat dengan mudah melihat masalah pembulatan yang dilakukan sql server saat Anda menggunakan DATETIME
tipe data.
select '2015-07-27 00:00:00.000' as Original_startDateTime,
convert(datetime ,'2015-07-27 00:00:00.000') as startDateTime,
'2015-07-27 23:59:59.999' as Original_endDateTime,
convert(datetime ,'2015-07-27 23:59:59.999') as endDateTime,
'2015-07-27 00:00:00.000' as Original_startDateTime2,
convert(datetime2 ,'2015-07-27 00:00:00.000') as startDateTime2, -- default precision is 7
'2015-07-27 23:59:59.999' as Original_endDateTime2,
convert(datetime2 ,'2015-07-27 23:59:59.999') as endDateTime2 -- default precision is 7
DATETIME2
sudah ada sejak SQL Server 2008, jadi mulailah menggunakannya DATETIME
. Untuk situasi Anda, Anda dapat menggunakan datetime2
dengan 3 desimal misalnya datetime2(3)
.
Manfaat Menggunakan datetime2
:
datetime
hanya mendukung 3 tempat desimal .. dan karenanya Anda melihat masalah pembulatan karena secara default membulatkan datetime
yang terdekat .003 seconds
dengan selisih .000
, .003
atau .007
detik.datetime2
jauh lebih tepat daripada datetime
dan datetime2
memberi Anda kontrol DATE
dan TIME
sebagai lawan datetime
.Referensi:
DateTime2
vs DateTime
.: a. Untuk - kasus - mayoritas - dari - penggunaan - dunia - nyata - kasus, manfaat dari DateTime2
Banyak biaya. Lihat: stackoverflow.com/questions/1334143/... b. Itu bukan akar masalah di sini. Lihat komentar selanjutnya.
datetime3
dengan 70 (vs 7) digit presisi ditambahkan?). Praktik terbaik adalah menggunakan nilai di mana presisi tidak menjadi masalah, yaitu < awal detik, menit, jam atau hari berikutnya vs. <= akhir detik, menit, jam atau hari sebelumnya.
Seperti beberapa orang lain telah disebutkan dalam komentar dan jawaban lain untuk pertanyaan Anda masalah inti 2015-07-27 23:59:59.999
sedang dibulatkan 2015-07-28 00:00:00.000
oleh SQL Server. Per dokumentasi untuk DATETIME:
Rentang waktu - 00:00:00 hingga 23: 59: 59,997
Perhatikan bahwa rentang waktu tidak akan pernah bisa .999
. Lebih jauh ke bawah dalam dokumentasi itu menentukan aturan pembulatan yang menggunakan SQL Server untuk digit paling signifikan.
Perhatikan bahwa digit paling signifikan hanya dapat memiliki satu dari tiga nilai potensial: "0", "3", atau "7".
Ada beberapa solusi / solusi untuk ini yang dapat Anda gunakan.
-- Option 1
SELECT
*
FROM A
WHERE posted_date >= '2015-07-27 00:00:00.000'
AND posted_date < '2015-07-28 00:00:00.000' --Round up and remove equality
-- Option 2
SELECT
*
FROM A
WHERE posted_date >= '2015-07-27 00:00:00.000'
AND posted_date <= '2015-07-27 23:59:59.997' --Round down and keep equality
-- Option 3
SELECT
*
FROM A
WHERE CAST(posted_date AS DATE) = '2015-07-27' -- Use different data type
-- Option 4
SELECT
*
FROM A
WHERE CONVERT(CHAR(8), DateColumn, 112) = '20150727' -- Cast to string stripping off time
-- Option 5
SELECT
*
FROM A
WHERE posted_date BETWEEN '2015-07-27 00:00:00.000'
AND '2015-07-27 23:59:59.997' --Use between
Dari lima opsi yang saya sajikan di atas, saya akan mempertimbangkan opsi 1 dan 3 satu-satunya opsi yang layak. Mereka menyampaikan maksud Anda dengan jelas, dan tidak akan rusak jika Anda memperbarui tipe data. Jika Anda menggunakan SQL Server 2008 atau yang lebih baru, saya pikir opsi 3 harus menjadi pendekatan pilihan Anda. Itu terutama benar jika Anda bisa berubah dari menggunakan DATETIMEtipe data ke tipe DATEdata untuk posted_date
kolom Anda .
Mengenai opsi 3, penjelasan yang sangat baik tentang beberapa masalah dapat ditemukan di sini: Cast to date itu pantas, tetapi apakah itu ide yang bagus?
Saya tidak suka opsi 2 dan 5 karena .997
detik pecahan akan menjadi angka ajaib lain yang orang ingin "perbaiki." Untuk beberapa alasan mengapa BETWEEN
tidak dipeluk secara luas, Anda mungkin ingin memeriksa kiriman ini .
Saya tidak suka opsi 4 karena mengubah tipe data menjadi string untuk tujuan perbandingan terasa kotor bagi saya. Alasan yang lebih kualitatif untuk menghindarinya dalam SQL Server adalah dampak sargability alias Anda tidak dapat melakukan pencarian indeks dan itu akan sering mengakibatkan kinerja yang lebih buruk.
Untuk informasi lebih lanjut tentang cara yang benar dan cara yang salah untuk menangani pertanyaan rentang tanggal checkout posting ini oleh Aaron Bertrand .
Pada saat berpisah Anda akan dapat menjaga permintaan asli Anda dan itu akan berperilaku seperti yang diinginkan jika Anda mengubah posted_date
kolom Anda dari a DATETIMEke DATETIME2(3)
. Itu akan menghemat ruang penyimpanan di server, memberi Anda akurasi yang lebih besar pada presisi yang sama, lebih memenuhi standar / portable, dan memungkinkan Anda untuk dengan mudah menyesuaikan akurasi / presisi jika kebutuhan Anda berubah di masa depan. Namun, ini hanya opsi jika Anda menggunakan SQL Server 2008 atau yang lebih baru.
Sebagai sedikit hal-hal sepele 1/300
akurasi kedua dengan DATETIMEtampaknya menjadi pegangan dari UNIX per jawaban StackOverflow ini . Sybase yang memiliki warisan bersama memiliki kesamaan1/300
keakuratan yang sama dalam jenis DATETIME
danTIME
datanya, tetapi digit paling tidak signifikan mereka adalah sentuhan yang berbeda pada "0", "3", dan "6". Menurut pendapat saya 1/300
akurasi kedua dan / atau 3.33ms adalah keputusan arsitektur yang tidak menguntungkan karena blok 4 byte untuk waktu dalam DATETIMEtipe data SQL Server dapat dengan mudah mendukung akurasi 1 ms.
datetime3
dengan 70 (vs 7) digit presisi ditambahkan? Praktik terbaik adalah menggunakan nilai di mana ketepatan tidak penting, yaitu <awal detik, menit, jam atau hari berikutnya vs. <= akhir detik, menit, jam atau hari sebelumnya.
Konversi Tersirat
Saya kira tipe data yang diposting adalah tanggal. Namun tidak masalah apakah tipe di sisi lain adalah Datetime, Datetime2 atau just Time karena string (Varchar) akan secara implisit dikonversi ke Datetime.
Dengan diposting_date yang dinyatakan sebagai Datetime2 (atau Waktu), posted_date <= '2015-07-27 23:59:59.99999'
klausa gagal di mana karena meskipun 23:59:59.99999
merupakan nilai Datetime2 yang valid, ini bukan nilai Datetime yang valid:
Conversion failed when converting date and/or time from character string.
Rentang Waktu untuk Datetime
Rentang waktu Datetime adalah 00:00:00 hingga 23: 59: 59,997. Karenanya 23: 59: 59.999 berada di luar jangkauan dan harus dibulatkan ke atas atau ke bawah ke nilai terdekat.
Ketepatan
Selain itu nilai Datetime dibulatkan dengan peningkatan 0,000, 0,003, atau 0,007 detik. (mis. 000, 003, 007, 010, 013, 017, 020, ..., 997)
Ini tidak terjadi dengan nilai 2015-07-27 23:59:59.999
yang berada dalam kisaran ini: 2015-07-27 23:59:59.997
dan2015-07-28 0:00:00.000
.
Rentang ini sesuai dengan opsi sebelumnya dan berikut yang terdekat, keduanya berakhir dengan, 000, 0,003 atau 0,007.
Membulatkan ke atas atau ke bawah ?
Karena lebih dekat ke 2015-07-28 0:00:00.000
(1 vs -2) dari 2015-07-27 23:59:59.997
, string dibulatkan dan menjadi nilai Datetime ini: 2015-07-28 0:00:00.000
.
Dengan batas atas seperti 2015-07-27 23:59:59.998
(atau .995, .996, .997, .998), batas itu akan dibulatkan ke bawah 2015-07-27 23:59:59.997
dan kueri Anda akan berfungsi seperti yang diharapkan. Namun itu tidak akan menjadi solusi tetapi hanya nilai keberuntungan.
Jenis datetime2 atau Waktu
Datetime2 dan Rentang waktu waktu 00:00:00.0000000
dilewati23:59:59.9999999
dengan akurasi 100ns (digit terakhir saat digunakan dengan ketepatan 7 digit).
Namun rentang Datetime (3) tidak sama dengan rentang Datetime:
0:0:00.000
ke23:59:59.997
0:0:00.000000000
ke23:59:59.999
Larutan
Pada akhirnya lebih aman untuk mencari tanggal di bawah pada hari berikutnya daripada tanggal di bawah atau sama dengan apa yang Anda anggap sebagai fragmen terakhir dari waktu dalam sehari. Ini terutama karena Anda tahu bahwa hari berikutnya selalu dimulai pada 0: 00: 00.000 tetapi tipe data yang berbeda mungkin tidak memiliki waktu yang sama di akhir hari:
Datetime `0:0:00.000` to `23:59:59.997`
Datetime2 `0:0:00.000000000` to `23:59:59.999-999-900`
Time2 `0:0:00.000000000` to `23:59:59.999-999-900`
< 2015-07-28 0:00:00.000
akan memberi Anda akurat hasil yang dan merupakan pilihan terbaik<= 2015-07-27 23:59:59.xxx
dapat mengembalikan nilai yang tidak terduga ketika tidak dibulatkan ke apa yang Anda pikirkan seharusnya.Kita dapat berpikir bahwa mengubah [posted_date] ke Datetime2 dan presisi yang lebih tinggi dapat memperbaiki masalah ini, tetapi itu tidak akan membantu karena string masih dikonversi ke Datetime. Namun, jika gips ditambahkan cast(2015-07-27 23:59:59.999' as datetime2)
, ini berfungsi dengan baik
Transmisikan dan Konversi
Pemain dapat mengonversi nilai hingga 3 digit ke Datetime atau dengan hingga 9 digit ke Datetime2 atau Waktu dan membulatkannya ke presisi yang benar.
Perlu dicatat bahwa pemeran Datetime2 dan Time2 dapat memberikan hasil yang berbeda:
select cast('20150101 23:59:59.999999999' as datetime2(7))
adalah pembulatan 2015-05-03 00: 00: 00.0000000 (untuk nilai lebih dari 999999949)select cast('23:59:59.999999999' as time(7))
=> 23: 59: 59.9999999Ini semacam memperbaiki masalah yang dialami datetime dengan kenaikan 0, 3 dan 7 meskipun masih selalu lebih baik untuk mencari tanggal sebelum nano 1 detik di hari berikutnya (selalu 0: 00: 00.000).
Sumber MSDN: datetime (Transact-SQL)
Itu pembulatan
select cast('2015-07-27 23:59:59.999' as datetime)
returns 2015-07-28 00:00:00.000
.998, .997, .996, .995 semuanya dilemparkan ke .997
Harus menggunakan
select *
from A
where posted_date >= '2015-07-27 00:00:00.000'
and posted_date < '2015-07-28 00:00:00.000'
atau
where cast(posted_date as date) = '2015-07-27'
Lihat keakuratan dalam tautan ini
Selalu dilaporkan sebagai .000, .003, .007
select * from A where date(posted_date) = '2015-07-27'
'DATE' is not a recognized built-in function name.
gives you control of DATE and TIME as opposed to datetime.
apa artinya?