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, DATETIMEveri 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
DATETIME2SQL Server 2008'den beri var, bunun yerine kullanmaya başlayın DATETIME. Durumunuza için kullanabilirsiniz datetime2ile 3 ondalık sayılar hassasiyeti örn datetime2(3).
Kullanmanın Yararları datetime2:
datetimesadece 3 ondalık basamak destekleyen .. ve dolayısıyla varsayılan tarafından beri yuvarlama sorunu bakınız datetimemermi yakın .003 secondsartışlarla birlikte .000, .003ya da .007saniyeler.datetime2çok daha hassastır datetimeve datetime2size kontrol DATEve TIMEaksine datetime.Referans :
DateTime2vs. kullanarak DateTime: a. İçin - - engin - çoğunluğun - of - gerçek - Dünya - kullanım - vakalar faydaları DateTime2Kadar <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.999yuvarlanı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ü .997kesirli saniye, insanların "düzeltmek" isteyeceği başka bir sihirli sayı olacak. Bazı nedenlerden ötürü BETWEENyaygı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_datebir 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/300ikinci 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/300DATETIMETIME1/300DATETIME
datetime370 (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.999aralıktaki değerde durum böyle değildir : 2015-07-27 23:59:59.997ve 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.000daha (+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.997ve 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.0000000yoluyla 23:59:59.9999999100ns 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.000için23:59:59.9970: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.000size 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?