どれ:
SQL Server 2008+で日付と時刻を保存するための推奨される方法は何ですか?
精度(およびおそらくストレージスペース)の違いを認識していますが、今のところそれらを無視して、いつ何を使用するかについてのベストプラクティスドキュメントがありますか、それとも単に使用する必要がありますdatetime2
か?
datetimeのMSDNドキュメントでは、datetime2の使用を推奨しています。ここに彼らの推薦があります:
新しい作業には
time
、、、および データ型を使用します。これらのタイプはSQL標準に準拠しています。彼らはよりポータブルです。 、および より多くの秒の精度を提供します。 グローバルに展開されたアプリケーションのタイムゾーンサポートを提供します。date
datetime2
datetimeoffset
time
datetime2
datetimeoffset
datetimeoffset
datetime2には、より大きな日付範囲、より大きなデフォルトの小数精度、およびオプションのユーザー指定の精度があります。また、ユーザー指定の精度によっては、使用するストレージが少なくなる場合があります。
DATETIME2
日付範囲は「0001/01/01」から「9999/12/31」ですが、DATETIME
タイプは1753-9999年のみをサポートします。
また、必要に応じて、DATETIME2
時間の観点からより正確にすることができます。DATETIMEは31/3ミリ秒に制限されていますが、 DATETIME2
100nsまで正確にすることができます。
どちらのタイプも.NETにマップさSystem.DateTime
れます-違いはありません。
選択肢があれば、DATETIME2
可能な限り使用することをお勧めします。DATETIME
(下位互換性を除いて)使用する利点は見当たりません-問題が少なくなります(日付が範囲外であり、そのような面倒な作業があります)。
さらに、日付のみ(時間部分なし)が必要な場合は、DATEを使用してください。これは、スペースと同じくらい優れてDATETIME2
おり、スペースも節約できます。:-)同じことが時間だけに当てはまります-を使用してTIME
ください。それがこれらのタイプの目的です!
datetime2は、(古いアプリの互換性)を除くほとんどの面で勝ちます
以下の点にご注意ください
イメージソース: MCTSセルフペーストレーニングキット(試験70-432):Microsoft®SQLServer®2008-実装と保守 第3章:テーブル->レッスン1:テーブルの作成->66ページ
私は@marc_sと@Adam_Powardに同意します-DateTime2は前進するための好ましい方法です。日付の範囲が広く、精度が高く、使用するストレージは同等以下です(精度によって異なります)。
しかし、議論が見逃したことの1つは...
@Marc_sは次のように述べていますBoth types map to System.DateTime in .NET - no difference there
。これは正しいですが、その逆は当てはまりません...そして、日付範囲検索を行うときに重要です(たとえば、「2010年5月5日に変更されたすべてのレコードを検索する」)。
.NETのバージョンは、 Datetime
と同様の範囲と精度を備えていDateTime2
ます。.netDatetime
を古いSQLにマッピングするとDateTime
、暗黙的な丸めが発生します。古いSQLDateTime
は3ミリ秒まで正確です。これは11:59:59.997
、1日の終わりにできるだけ近いことを意味します。それより高いものは翌日に切り上げられます。
これを試して :
declare @d1 datetime = '5/5/2010 23:59:59.999'
declare @d2 datetime2 = '5/5/2010 23:59:59.999'
declare @d3 datetime = '5/5/2010 23:59:59.997'
select @d1 as 'IAmMay6BecauseOfRounding', @d2 'May5', @d3 'StillMay5Because2msEarlier'
この暗黙的な丸めを回避することは、DateTime2に移行する重要な理由です。日付の暗黙的な丸めは明らかに混乱を引き起こします:
ほとんどすべての回答とコメントは、長所に重く、短所に軽いものでした。これは、これまでのすべての長所と短所に加えて、いくつかの重要な短所(以下の#2)の要約です。
1.1。よりISO準拠(ISO 8601)(ただし、これが実際にどのように機能するかはわかりません)。
1.2。より多くの範囲(1/1/0001から12/31/9999対1/1/1753-12/31/9999)(ただし、1753年より前の追加の範囲は、例を除いて使用されない可能性があります。歴史的、天文学的、地質学的などのアプリで)。
1.3。.NETのDateTime
タイプの範囲の範囲と完全に一致します(ただし、値がターゲットタイプの範囲と精度の範囲内にある場合は、特別なコーディングなしで前後に変換されます。ただし、以下のCon#2.1を除き、エラー/丸めが発生します)。
1.4。より高い精度(100ナノ秒別名0.000,000,1秒vs. 3.33ミリ秒別名0.003,33秒)(ただし、エンジニアリング/科学アプリなどを除いて、追加の精度は使用されない可能性があります)。
1.5。同様の(Iman Abidiが主張したように「同じ」ではない(3.33ミリ秒のように)1ミリ秒のように)精度を設定するとDateTime
、使用するスペースは少なくなります(7バイトと8バイト)が、もちろん、精度のメリットは、おそらく不要なメリットではありますが、2つのうちの1つ(もう1つは範囲)で最も宣伝されています)。
2.1。パラメータを.NETに渡すときは、SQL Serverの範囲や精度の範囲外の値を渡す可能性があるかどうかをSqlCommand
指定する必要があります。これは、デフォルトで.NETになっているためです。System.Data.SqlDbType.DateTime2
DateTime
System.Data.SqlDbType.DateTime
2.2。浮動小数点数値(最小日時からの日数)値に暗黙的/簡単に変換して、数値と演算子を使用してSQLServer式で次のことを実行することはできません。
2.2.1。日数または部分日数を加算または減算します。注:DateAdd
日時のすべてではないにしても複数の部分を考慮する必要がある場合、回避策として関数を使用することは簡単ではありません。
2.2.2。「年齢」の計算のために、2つの日時の差を取ります。注:SQL ServerのDateDiff
関数を代わりに使用することはできません。これはage
、2つの日時が、ごくわずかであっても、指定された単位のカレンダー/時計の日時の境界を超えた場合に、ほとんどの人が期待するように計算されないためです。その単位の差は、その単位の1対0として返されます。たとえば、わずか1ミリ秒離れた2つの日時のDateDiff
inは、それらの日時が次の場合、1対0(日)を返します。Day
異なる暦日(つまり、「1999-12-31 23:59:59.9999999」および「2000-01-0100:00:00.0000000」)。同じ1ミリ秒の差の日時は、暦日を超えないように移動した場合Day
、0(日)の'sで「DateDiff」を返します。
2.2.3。Avg
最初に「Float」に変換してから、もう一度に戻すことで、(集計クエリで)日時を取得しDateTime
ます。
注:DateTime2
数値に変換するには、次の数式のように、値が1970年以上であると想定する必要があります(つまり、余分な範囲とさらに217年がすべて失われます。注:数値オーバーフローの問題が発生する可能性があるため、数式を調整して範囲を追加することはできません。
25567 + (DATEDIFF(SECOND, {d '1970-01-01'}, @Time) + DATEPART(nanosecond, @Time) / 1.0E + 9) / 86400.0
–ソース:「https://siderite.dev/blog/how-to-translate-t-sql-datetime2-to.html」</ p>
もちろん、最初に(そして必要に応じて再びに戻る)Cast
することもできますが、対の精度と範囲(すべて1753年より前)の利点が失われます。足し算/引き算/「年齢」(対)/大きなものである計算の利点のために浮動小数点数値(日数)への暗黙的/簡単な変換を失うときになぜそれを使用するのかという疑問を投げかける最も必要性の低い2つ私の経験では。DateTime
DateTime2
DateTime2
DateTime
DateDiff
Avg
ところで、日時のは重要なユースケースですAvg
(または少なくともそうあるべきです)。a)日時(共通の基準日時)が期間(一般的な慣行)を表すために使用される場合の平均期間の取得に使用する以外に、b)平均日時に関するダッシュボードタイプの統計を取得することも役立ちます-時間は、行の範囲/グループの日時列にあります。c)これまで/もはや有効ではない可能性があるおよび/または非推奨になる可能性がある列の値を監視/トラブルシューティングするための標準(または少なくとも標準である必要があります)アドホッククエリは、各値の発生回数を一覧表示することですおよび(使用可能な場合)Min
、Avg
およびMax
その値に関連付けられた日時スタンプ。
以下は、smalldatetime、datetime、datetime2(0)、およびdatetime2(7)間のストレージサイズ(バイト)と精度の違いを示す例です。
DECLARE @temp TABLE (
sdt smalldatetime,
dt datetime,
dt20 datetime2(0),
dt27 datetime2(7)
)
INSERT @temp
SELECT getdate(),getdate(),getdate(),getdate()
SELECT sdt,DATALENGTH(sdt) as sdt_bytes,
dt,DATALENGTH(dt) as dt_bytes,
dt20,DATALENGTH(dt20) as dt20_bytes,
dt27, DATALENGTH(dt27) as dt27_bytes FROM @temp
これは
sdt sdt_bytes dt dt_bytes dt20 dt20_bytes dt27 dt27_bytes
------------------- --------- ----------------------- -------- ------------------- ---------- --------------------------- ----------
2015-09-11 11:26:00 4 2015-09-11 11:25:42.417 8 2015-09-11 11:25:42 6 2015-09-11 11:25:42.4170000 8
したがって、ミリ秒ではなく秒単位で情報を格納したい場合、datetimeまたはdatetime2(7)の代わりにdatetime2(0)を使用すると、それぞれ2バイト節約できます。
DateTime2 wreaks havoc if you are an Access developer trying to write Now() to the field in question. Just did an Access -> SQL 2008 R2 migration and it put all the datetime fields in as DateTime2. Appending a record with Now() as the value bombed out. It was okay on 1/1/2012 2:53:04 PM, but not on 1/10/2012 2:53:04 PM.
Once character made the difference. Hope it helps somebody.
古い質問...しかし、ここにまだ誰も述べていないことを追加したい...(注:これは私自身の観察なので、参照を求めないでください)
Datetime2は、フィルター基準で使用すると高速になります。
TLDR:
SQL 2016では、秒までの正確な時刻を格納する必要があるため、10万行と日時列ENTRY_TIMEのテーブルがありました。多くの結合とサブクエリを含む複雑なクエリを実行しているときに、where句を次のように使用した場合:
WHERE ENTRY_TIME >= '2017-01-01 00:00:00' AND ENTRY_TIME < '2018-01-01 00:00:00'
クエリは、最初は数百行ある場合は問題ありませんでしたが、行数が増えると、クエリで次のエラーが発生し始めました。
Execution Timeout Expired. The timeout period elapsed prior
to completion of the operation or the server is not responding.
where句を削除しましたが、予期せず、クエリは1秒で実行されましたが、すべての日付のすべての行がフェッチされました。where句を使用して内部クエリを実行すると85秒かかり、where句を使用しない場合は0.01秒かかりました。
日時フィルタリングのパフォーマンスとして、この問題についてここで多くのスレッドに出くわしました
クエリを少し最適化しました。しかし、私が得た実際の速度は、datetime列をdatetime2に変更することでした。
これで、以前にタイムアウトした同じクエリに1秒もかかりません。
乾杯
米国以外の設定を使用する場合、日付文字列の解釈も異なる場合がdatetime
あります。例えばdatetime2
DATEFORMAT
set dateformat dmy
declare @d datetime, @d2 datetime2
select @d = '2013-06-05', @d2 = '2013-06-05'
select @d, @d2
2013-05-06
これは、に対して(つまり、 5月6日)、。に対して(つまり、6datetime
月5日)を返します。ただし、に設定すると、両方とが返されます。2013-06-05
datetime2
dateformat
mdy
@d
@d2
2013-06-05
このdatetime
動作は、次のように記載されているMSDNドキュメントと矛盾しているようSET DATEFORMAT
です。ISO8601などの一部の文字列形式は、DATEFORMAT設定とは無関係に解釈されます。明らかに真実ではありません!
yyyy-mm-dd
これに噛まれるまでは、言語やロケールの設定に関係なく、日付は正しく処理されると常に思っていました。
datetime2を使用すると精度が向上しますが、一部のクライアントはdate、time、またはdatetime2をサポートしておらず、文字列リテラルに変換する必要があります。具体的には、Microsoftは、これらのデータ型に関する「ダウンレベル」のODBC、OLE DB、JDBC、およびSqlClientの問題に言及し、それぞれが型をマップする方法を示す グラフを用意しています。
精度よりも互換性を重視する場合は、日時を使用してください
この記事によると、DateTime2を使用してDateTimeの精度を同じにしたい場合は、DateTime2(3)を使用する必要があります。これにより、同じ精度が得られ、使用するバイト数が1つ少なくなり、範囲が拡張されます。
私はもう1つの利点に出くわしましたDATETIME2
:Pythonモジュールのバグを回避します。これは、列にゼロ以外のマイクロ秒を持つadodbapi
標準ライブラリ値が渡された場合に爆発しますが、列がとして定義されている場合は正常に機能します。datetime
DATETIME
DATETIME2
他の回答が示すように、datetime2
サイズが小さく精度が高いため推奨されますが、NikolaIlicのdatetime2を使用しない理由についていくつか考えます。
GETDATE()+1
DATEADD
またはとの比較を行うたびにDATEDIFF
、暗黙のデータ変換を終了します。datetime
DATETIME2
を保存する方date
が効率が良いので、より良い方法だと思いますDATETIME
。SQL Server 2008
を使用できる場合DATETIME2
、日付と時刻を格納し、格納に6〜8かかり、bytes
精度は100 nanoseconds
。です。したがって、より高い時間精度が必要な人は誰でも欲しいでしょうDATETIME2
。
受け入れられた答えは素晴らしいです。フロントエンドにDateTime2を送信している場合は、通常のDateTimeに相当する値に丸められることを知っておいてください。
私のソリューションでは、送信されたものと再送信されたときにデータベースにあるものを比較する必要があり、単純な比較'=='では丸めができなかったため、これは私にとって問題を引き起こしました。したがって、追加する必要がありました。
Select ValidUntil + 1
from Documents
上記のSQLは、DateTime2フィールドでは機能しません。「オペランドタイプの衝突:datetime2はintと互換性がありません」というエラーが返されます。
翌日を取得するために1を追加することは、開発者が何年にもわたって日付を使用して行ってきたことです。現在、Microsoftには、この単純な機能を処理できない非常に新しいdatetime2フィールドがあります。
「古いタイプよりも悪いこの新しいタイプを使いましょう」、私はそうは思いません!