select *
from A
where posted_date >= '2015-07-27 00:00:00.000'
and posted_date <= '2015-07-27 23:59:59.999'
しかし、結果には本日投稿されたレコードが含まれています:2015-07-28。データベースサーバーが私の国にありません。何が問題ですか ?
あなたが使っているので datetime データ型では、SQLサーバーが日時データを丸める方法を理解する必要があります。
_╔═══════════╦═════╦═════════════════════════════╦═════════════════════════════╦══════════╦═══════════╗
║ 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 ║
╚═══════════╩═════╩═════════════════════════════╩═════════════════════════════╩══════════╩═══════════╝
_
以下のクエリを使用すると、DATETIME
データ型を使用するときにSQLサーバーが行う丸めの問題を簡単に確認できます。
_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以降で使用されているため、DATETIME
の代わりに使用してください。あなたの状況では、_datetime2
_を3桁の精度で使用できます。 datetime2(3)
。
_datetime2
_を使用する利点:
datetime
は小数点以下3桁のみをサポートします。したがって、デフォルトではdatetime
は最も近い_.003 seconds
_を次のように丸めるため、丸めの問題が発生します。 _.000
_、_.003
_または_.007
_秒。datetime2
_はdatetime
よりはるかに正確であり、_datetime2
_はDATE
ではなくTIME
およびdatetime
を制御できます。参照 :
他のいくつかのコメントやその他の質問への回答で言及したように、コアの問題は_2015-07-27 23:59:59.999
_がSQL Serverによって_2015-07-28 00:00:00.000
_に丸められていることです。 ドキュメントごと for DATETIME:
時間範囲-00:00:00から23:59:59.997
時間範囲は決して_.999
_になることはできません。ドキュメントのさらに下の方で、SQL Serverが最下位桁に使用する丸め規則を指定しています。
最下位桁は、「0」、「3」、または「7」の3つの潜在的な値のいずれか1つしか持てないことに注意してください。
このために使用できるいくつかの解決策/回避策があります。
_-- 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
_
上記で示した5つのオプションのうち、オプション1と3のみを実行可能なオプションと見なします。それらはあなたの意図を明確に伝え、データ型を更新しても壊れることはありません。 SQL Server 2008以降を使用している場合は、オプション3をお勧めします。これは、使用を変更できる場合は特に当てはまります。 DATETIME データ型を DATE _posted_date
_列のデータ型。
オプション3に関しては、いくつかの問題についての非常に良い説明がここにあります: これまでのキャストはargableですが、それは良いアイデアですか?
_.997
_小数点以下の秒数が別の マジックナンバー になるため、オプション2と5は好きではありません。 BETWEEN
が広く採用されていないいくつかの理由により、チェックアウトすることをお勧めします この投稿 。
比較のためにデータ型を文字列に変換するのは不愉快に思うので、オプション4は好きではありません。 SQL Serverでこれを回避するより定性的な理由は、sargabilityに影響することです。別名、インデックスシークを実行できず、パフォーマンスが低下することがよくあります。
日付範囲クエリを処理する正しい方法と間違った方法の詳細については、チェックアウト この投稿 by Aaron Bertrand を参照してください。
別れでは、元のクエリを維持することができ、_posted_date
_列を DATETIME DATETIME2(3)
に変換します。これにより、サーバーのストレージスペースが節約され、同じ精度でより高い精度が得られ、より標準に準拠/ポータブルになり、将来ニーズが変化した場合に精度/精度を簡単に調整できます。ただし、これはSQL Server 2008以降を使用している場合のオプションにすぎません。
ちょっとした雑学として、_1/300
_を2番目の精度で DATETIME UNIXからのホールドオーバーであるようです このStackOverflowの回答ごとに 。 共有遺産 を持つSybaseは、同様の_1/300
_の DATETIME
およびTIME
データ型に2番目の精度がありますが、それらの最下位桁は、「0」、「3」、および「6」で少し異なります。私の意見では、秒(および3.33ms)の精度の_1/300
_は、SQL Serverの 4バイトブロックの時間 なので、不幸なアーキテクチャ上の決定です。 DATETIME データ型は1msの精度を簡単にサポートできます。
暗黙的な変換
私は、posted_dateデータ型がDatetimeであることを想定しています。ただし、文字列(Varchar)は暗黙的にDatetimeに変換されるため、反対側の型がDatetime、Datetime2、または単なるTimeであるかどうかは関係ありません。
Posted_dateがDatetime2(またはTime)として宣言されている場合、_posted_date <= '2015-07-27 23:59:59.99999'
_は有効なDatetime2値ですが、これは有効なDatetime値ではないため、_23:59:59.99999
_ where句は失敗します。
_ Conversion failed when converting date and/or time from character string.
_
日時の時間範囲
日時の時間範囲は00:00:00から23:59:59.997です。したがって、23:59:59.999は範囲外であり、最も近い値に切り上げるか切り捨てる必要があります。
精度
さらに、日時の値は、.000、.003、または.007秒の増分で丸められます。 (つまり、000、003、007、010、013、017、020、...、997)
これは、_2015-07-27 23:59:59.999
_と_2015-07-27 23:59:59.997
_の範囲内にある値_2015-07-28 0:00:00.000
_には当てはまりません。
この範囲は、最も近い先行オプションと後続オプションに対応し、どちらも.000、.003、または.007で終わります。
切り上げまたは切り下げ?
_2015-07-28 0:00:00.000
_よりも_2015-07-27 23:59:59.997
_(+1対-2)に近いため、文字列は切り上げられ、日時(_2015-07-28 0:00:00.000
_)の値になります。
_2015-07-27 23:59:59.998
_(または.995、.996、.997、.998)のような上限では、_2015-07-27 23:59:59.997
_に切り捨てられ、クエリは期待どおりに機能します。しかし、それは解決策ではなく、単なる幸運な価値でした。
Datetime2またはTimeタイプ
Datetime2とTimeの時間範囲は、_00:00:00.0000000
_から_23:59:59.9999999
_までの精度で100ns(7桁の精度で使用した場合の最後の桁)です。
ただし、Datetime(3)の範囲は、Datetimeの範囲とは異なります。
0:0:00.000
_から_23:59:59.997
_0:0:00.000000000
_から_23:59:59.999
_ソリューション
結局、次の日付よりも下の日付を探す方が、それがその日の最後の時間の断片と同じか、それよりも安全です。これは主に、翌日は常に0:00:00.000から始まるが、異なるデータタイプがその日の終わりに同じ時刻を持たない可能性があることがわかっているためです。
_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
_はaccurateの結果を提供し、最良の選択肢<= 2015-07-27 23:59:59.xxx
_は、想定どおりに切り上げられない場合、予期しない値を返すことがあります。[posted_date]をDatetime2に変更し、より高い精度でこの問題を解決できると考えられますが、文字列がまだDatetimeに変換されているため、役に立ちません。ただし、キャストが追加された場合cast(2015-07-27 23:59:59.999' as datetime2)
、これは正常に動作します
キャストと変換
Castは、最大3桁の値をDatetimeに、または最大9桁の値をDatetime2またはTimeに変換し、正しい精度に丸めることができます。
Datetime2とTime2のキャストでは異なる結果が得られる場合があることに注意してください。
select cast('20150101 23:59:59.999999999' as datetime2(7))
は切り上げられます2015-05-03 00:00:00.0000000(999999949より大きい値の場合)select cast('23:59:59.999999999' as time(7))
=> 23:59:59.9999999日付時刻が0、3、7の増分で発生する問題をある程度修正しますが、次の日の最初のナノ秒(常に0:00:00.000)より前の日付を探す方が常に良いです。
ソースMSDN: datetime(Transact-SQL)
丸めです
select cast('2015-07-27 23:59:59.999' as datetime)
returns 2015-07-28 00:00:00.000
.998、.997、.996、.995すべてキャスト/ラウンドして.997
使用する必要があります
select *
from A
where posted_date >= '2015-07-27 00:00:00.000'
and posted_date < '2015-07-28 00:00:00.000'
または
where cast(posted_date as date) = '2015-07-27'
これの正確さをご覧ください link
常に.000、.003、.007として報告されます