回答:
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
。参照 :
DateTime2
対を使用してDateTime
:a。のために- 広大な-過半数 -の- 現実-世界 -使用-ケース、メリットDateTime2
<コスト。参照:stackoverflow.com/questions/1334143/…b 。ここでは根本的な問題ではありません。次のコメントを参照してください。
datetime3
、70(vs. 7)桁の精度が追加された場合)。ベストプラクティスは、精度がない問題、すなわち<ない値を使用することで、スタート次の秒、分、1時間または1日対<=の終了前に、秒、分、1時間または1日のを。
他のいくつかの人があなたの質問に対するコメントや他の回答で言及しているように、中心的な問題はSQL Serverによって2015-07-27 23:59:59.999
丸められ2015-07-28 00:00:00.000
ています。ドキュメントあたりのためにDATETIME:
時間範囲-00:00:00から23:59:59.997
時間範囲はにならないことに注意してください.999
。さらに下のドキュメントでは、SQL Serverが最下位桁に使用する丸め規則を指定しています。
最下位桁には、「0」、「3」、または「7」の3つの潜在的な値のいずれかしか指定できないことに注意してください。
これには、使用できる解決策/回避策がいくつかあります。
-- 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データ型に変更できる場合は特にそうですDATEposted_date
の列。
オプション3については、いくつかの問題に関する非常に良い説明がここにあります。 これまでのキャストは検索可能ですが、それは良い考えですか?
.997
秒の小数部は、人々が「修正」したい別の魔法の数になるため、オプション2と5は好きではありません。BETWEEN
広く受け入れられないいくつかの理由により、この投稿をチェックアウトすることをお勧めします。
比較4のためにデータ型を文字列に変換することは私にとって汚いので、オプション4は好きではありません。SQL Serverでこれを回避するより定性的な理由は、検索性に影響を与えることです。
日付範囲クエリを処理する正しい方法と間違った方法の詳細については、Aaron Bertrandによるこの投稿をご覧ください。
別れでは、あなたの元のクエリを維持することができるだろうと、それはあなたがあなたのご変更する場合は、必要に応じて振る舞うposted_date
から列をDATETIMEしますDATETIME2(3)
。これにより、サーバー上のストレージスペースが節約され、同じ精度でより高い精度が得られ、より多くの標準に準拠/移植可能になり、将来ニーズが変化した場合に精度/精度を簡単に調整できます。ただし、これは、SQL Server 2008以降を使用している場合のオプションにすぎません。
ちょっとした雑学として、このStackOverflowの回答による1/300
と、2番目の精度DATETIMEはUNIXからの引き継ぎのようです。共有の遺産を持つSybaseは、データ型とデータ型で2番目の精度に類似していますが、最下位桁は「0」、「3」、「6」でタッチが異なります。私の意見では、SQL Serverのデータ型の時間の4バイトブロックは1ミリ秒の精度を簡単にサポートできたため、 2番目および/または3.33ミリ秒の精度は不幸なアーキテクチャ上の決定です。1/300
DATETIME
TIME
1/300
DATETIME
datetime3
70と精度の(対7)数字が追加しましたか?ベストプラクティスは、精度が重要でない値、つまり<次の秒、分、時間、または日の開始対<=前の秒、分、時間、または日の終了です。
暗黙的な変換
Posted_dateデータ型はDatetimeであると想定しました。ただし、文字列(Varchar)は暗黙的にDatetimeに変換されるため、反対側の型がDatetime、Datetime2、または単なるTimeのいずれでもかまいません。
Posted_dateがDatetime2(またはTime)として宣言されているposted_date <= '2015-07-27 23:59:59.99999'
場合、altough 23:59:59.99999
は有効なDatetime2値であるため、where句は失敗しますが、これは有効なDatetime値ではありません。
Conversion failed when converting date and/or time from character string.
日時の時間範囲
Datetimeの時間範囲は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
(+1対-2)に近いため2015-07-27 23:59:59.997
、文字列は切り上げられ、このDatetime値:になり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と時刻の時間範囲は、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から始まるが、異なるデータ型は1日の終わりに同じ時刻にならない場合があることがわかっているためです。
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
正確な結果が得られ、最良のオプションです<= 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に変換して、正しい精度に丸めることができます。
Cast of 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:日時(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'
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.
どういう意味ですか?