Yanıtlar:
datetimeVeri türünü kullandığınızdan , sql sunucusunun datetime verilerini nasıl yuvarladığını anlamanız gerekir.
╔═══════════╦═════╦═════════════════════════════╦═════════════════════════════╦══════════╦═══════════╗
║ 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 ║
╚═══════════╩═════╩═════════════════════════════╩═════════════════════════════╩══════════╩═══════════╝
Aşağıdaki sorguyu kullanarak, DATETIME
veri türünü kullandığınızda sql sunucusunun yaptığı yuvarlama sorununu kolayca görebilirsiniz .
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
SQL Server 2008'den beri var, bunun yerine kullanmaya başlayın DATETIME
. Durumunuza için kullanabilirsiniz datetime2
ile 3 ondalık sayılar hassasiyeti örn datetime2(3)
.
Kullanmanın Yararları datetime2
:
datetime
sadece 3 ondalık basamak destekleyen .. ve dolayısıyla varsayılan tarafından beri yuvarlama sorunu bakınız datetime
mermi yakın .003 seconds
artışlarla birlikte .000
, .003
ya da .007
saniyeler.datetime2
çok daha hassastır datetime
ve datetime2
size kontrol DATE
ve TIME
aksine datetime
.Referans :
DateTime2
vs. kullanarak DateTime
: a. İçin - - engin - çoğunluğun - of - gerçek - Dünya - kullanım - vakalar faydaları DateTime2
Kadar <maliyetleri. Bkz . Stackoverflow.com/questions/1334143/… b. Buradaki temel sorun bu değil. Bir sonraki yoruma bakın.
datetime3
, 70 (vs 7) basamaklar hassasiyet eklenirse?). En iyi uygulama hassas, madde yani <değil bir değeri kullanmaktır başlangıç sonraki ikinci, dakika, saat veya gün vs. <= ait sonuna öncesinde ikinci, dakika, saat veya gün.
Bazılarının yorumlarda ve sorunuzun diğer cevaplarında bahsettiği gibi, asıl sorun SQL Server tarafından 2015-07-27 23:59:59.999
yuvarlanıyor 2015-07-28 00:00:00.000
. Dokümantasyon Başına için DATETIME:
Zaman aralığı - 00:00:00 - 23: 59: 59.997
Zaman aralığının asla olmayacağını unutmayın .999
. Belgelerde ayrıca SQL Server'ın en az anlamlı basamak için kullandığı yuvarlama kurallarını belirtir.
En az anlamlı basamak sadece üç potansiyel değerden birine sahip olabilir: "0", "3" veya "7".
Bunun için kullanabileceğiniz birkaç çözüm / geçici çözüm vardır.
-- 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
Yukarıda sunduğum beş seçenekten 1 ve 3 numaralı seçenekleri tek geçerli seçenekler olarak görüyorum. Niyetinizi açıkça ifade ederler ve veri türlerini güncellerseniz kırılmazlar. SQL Server 2008 veya daha yenisini kullanıyorsanız seçenek 3'ün tercih ettiğiniz yaklaşım olduğunu düşünüyorum. Bu, özellikle veri türünü sütununuz için DATETIMEbir DATEveri türüne dönüştürmekten başka bir şey yapamazsanız geçerlidir posted_date
.
Seçenek 3 ile ilgili olarak, bazı konular hakkında çok iyi bir açıklama burada bulunabilir: Bugüne kadar yayınlamak anlaşılabilir, ancak iyi bir fikir mi?
Seçenek 2 ve 5'i sevmiyorum çünkü .997
kesirli saniye, insanların "düzeltmek" isteyeceği başka bir sihirli sayı olacak. Bazı nedenlerden ötürü BETWEEN
yaygın olarak benimsenmemek için bu gönderiyi kontrol etmek isteyebilirsiniz .
Seçenek 4'ü sevmiyorum çünkü karşılaştırma amacıyla veri türlerini bir dizeye dönüştürmek bana kirli geliyor. SQL Server'da bundan kaçınmak için daha kalitatif bir sebep, bir dizin araması yapamayacağınız ve genellikle daha düşük performansla sonuçlanabilecek olan acizliği etkiler .
Tarih aralığı sorgularını işlemenin doğru ve yanlış yolu hakkında daha fazla bilgi için Aaron Bertrand tarafından gönderilen bu gönderiye göz atın .
Ayrılık size orijinal sorgu tutmak mümkün olacaktır ve size sizin değiştirirseniz istediğiniz gibi davranırdı posted_date
bir sütunu DATETIMEa DATETIME2(3)
. Bu, sunucuda depolama alanından tasarruf etmenizi sağlar, aynı hassasiyette size daha fazla doğruluk sağlar, daha fazla standartla uyumlu / taşınabilir olur ve gelecekte ihtiyaçlarınız değişirse doğruluk / hassasiyeti kolayca ayarlamanıza olanak tanır. Ancak, bu yalnızca SQL Server 2008 veya daha yenisini kullanıyorsanız bir seçenektir.
Biraz trivia olarak, 1/300
ikinci bir doğruluk ile bu StackOverflow cevap başınaDATETIME UNIX bir tutma gibi görünüyor . Paylaşılan bir mirasa sahip olan Sybase , kendi ve veri türlerinde ikinci bir doğruluğa benzer , ancak en az önemli basamakları "0", "3" ve "6" da farklı bir dokunuş. Bence ikinci ve / veya 3.33 ms doğruluk talihsiz bir mimari karardır çünkü SQL Server'ın veri tipindeki zaman için 4 baytlık blok 1 ms doğruluğu kolaylıkla destekleyebilirdi.1/300
DATETIME
TIME
1/300
DATETIME
datetime3
70 (7'ye karşı) basamak hassasiyeti eklenirse? En iyi uygulama, hassasiyetin önemli olmadığı bir değer kullanmaktır, yani <bir sonraki saniye, dakika, saat veya günün başlangıcı; <= önceki ikinci, dakika, saat veya günün sonu.
Örtük Dönüşüm
Posted_date veri türünün Datetime olduğunu varsayalım. Ancak, diğer taraftaki türün Datetime, Datetime2 veya Just Time olması önemli değildir, çünkü dize (Varchar) örtük olarak Datetime'a dönüştürülecektir.
Posted_date, Datetime2 (veya Time) olarak bildirildiğinde, geçerli bir Datetime2 değeri posted_date <= '2015-07-27 23:59:59.99999'
olduğu için where cümlesi başarısız olur 23:59:59.99999
, bu geçerli bir Datetime değeri değildir:
Conversion failed when converting date and/or time from character string.
Tarih için Zaman Aralığı
Tarih saat aralığı 00:00:00 ila 23: 59: 59.997'dir. Bu nedenle 23: 59: 59.999 aralık dışındadır ve en yakın değere aşağı veya yukarı yuvarlanmalıdır.
doğruluk
Ayrıca Datetime değerleri .000, .003 veya .007 saniyelik artışlarla yuvarlanır. (ör. 000, 003, 007, 010, 013, 017, 020, ..., 997)
Bu 2015-07-27 23:59:59.999
aralıktaki değerde durum böyle değildir : 2015-07-27 23:59:59.997
ve 2015-07-28 0:00:00.000
.
Bu aralık, her ikisi de .000, .003 veya .007 ile biten en yakın ve önceki seçeneklere karşılık gelir.
Yukarı veya aşağı yuvarlama ?
Bu daha yakın olduğu için 2015-07-28 0:00:00.000
daha (+1 karşı -2) 2015-07-27 23:59:59.997
: dizge yuvarlanır ve bu tarih saat değeri olur edilir 2015-07-28 0:00:00.000
.
2015-07-27 23:59:59.998
(Veya .995, .996, .997, .998) gibi bir üst sınırla, aşağı yuvarlanır 2015-07-27 23:59:59.997
ve sorgunuz beklendiği gibi çalışırdı. Ancak bir çözüm değil, sadece şanslı bir değer olurdu.
Tarih2 veya Saat türleri
Datetime2 ve Zaman zaman aralıkları olan 00:00:00.0000000
yoluyla 23:59:59.9999999
100ns doğrulukla (7 haneli bir hassasiyetle kullanılan son rakam) ile yıkanmıştır.
Ancak bir Datetime (3) aralığı, Datetime aralığına benzemez:
0:0:00.000
için23:59:59.997
0:0:00.000000000
-23:59:59.999
Çözüm
Sonunda, ertesi günün altında, aşağıdaki tarihlerden daha düşük ya da günün son parçası olduğunu düşündüğünüze eşit olan tarihleri aramak daha güvenlidir. Bunun nedeni, ertesi günün her zaman 0: 00: 00.000'da başladığını ancak farklı veri türlerinin günün sonunda aynı zamana sahip olmayabileceğini bilmenizdir:
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
size doğru sonuçlar verecektir ve en iyi seçenektir<= 2015-07-27 23:59:59.xxx
olması gerektiğini düşündüğünüz şekilde yuvarlanmadığında beklenmedik değerler döndürebilir.[Posted_date] değerini Datetime2 olarak değiştirmenin ve daha yüksek hassasiyetinin bu sorunu çözebileceğini düşünebiliriz, ancak dize hala Datetime'a dönüştürüldüğünden yardımcı olmaz. Ancak, bir kadro eklenirse cast(2015-07-27 23:59:59.999' as datetime2)
, bu iyi çalışır
Yayınla ve Dönüştür
Cast, 3 basamağa kadar bir değeri Datetime veya 9 basamağa kadar Datetime2 veya Time'a dönüştürebilir ve doğru hassasiyete yuvarlayabilir.
Datetime2 ve Time2 oyuncularının farklı sonuçlar verebileceği unutulmamalıdır:
select cast('20150101 23:59:59.999999999' as datetime2(7))
2015-05-03 00: 00: 00.0000000 (999999949'dan büyük değerler için)select cast('23:59:59.999999999' as time(7))
=> 23: 59: 59.9999999Bu, tarihin 0, 3 ve 7 artışlarla ilgili sorununu düzeltmekle birlikte, ertesi günün 1. nano saniyesinden önceki tarihleri aramak her zaman daha iyidir (her zaman 0: 00: 00.000).
Kaynak MSDN: datetime (Transact-SQL)
Yuvarlanıyor
select cast('2015-07-27 23:59:59.999' as datetime)
returns 2015-07-28 00:00:00.000
.998, .997, .996, .995 hepsi .997'ye kadar dökme / yuvarlak
Kullanmalı
select *
from A
where posted_date >= '2015-07-27 00:00:00.000'
and posted_date < '2015-07-28 00:00:00.000'
veya
where cast(posted_date as date) = '2015-07-27'
Bu bağlantıdaki doğruluğa bakın
Her zaman .000, .003, .007 olarak bildirilir
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.
Bu ne anlama geliyor?