DateTimeを保存する好ましい方法


18

日付と時刻の情報は、いくつかの方法で保存できます。DateTime情報を保存するための最良の方法は何ですか?

DateTimeを使用して2つの別々の列または1つの列に日付と時刻を保存しますか?

そのアプローチが優れている理由を説明できますか?

(参照用のMySQLドキュメントへのリンク。質問は一般的であり、MySQLに固有ではありません)
日付と時刻のタイプ:日付と時刻


3
これは、使用しているデータベースシステムに大きく依存します。その価値について:Oracleはこれを1列として(DATETIMEデータ型として)選択しましたが、その場合、組み込みサポートを使用することは、その情報を2列にNUMBERデータ型として保存するよりも確かに優れています特定のクエリに対して1つの部分が必要です...日付または時刻)。
クリスジョンストン

5
SQL Serverの場合、分割が優先される場合の1つは、日付によるグループ化です。ストリーム集約は、必要な順序を提供し ても、date,time with の複合インデックスのソートなしで使用できますが、withのインデックスには使用できgroup by dateません。datetimegroup by cast(datetime as date)
マーティンスミス

1
時間値の計算には日付とタイムゾーンを知る必要があることに注意してください。たとえば、2つの時間の間の距離は、その日がDSTイベントを含むか、一部の日は23時間または25時間であり、うるう秒も存在するかどうかによって異なります。
ペティス

回答:


23

データは密接にリンクされているため、データを単一の列に格納することをお勧めします。ある時点は、2つではなく、1つの情報です。

多くの製品で「舞台裏」で使用される日付/時刻データを保存する一般的な方法は、「日付」が10進値の整数部分で、「時刻」が小数である10進値に変換することです。値。したがって、1900-01-01 00:00:00は0.0として保存され、2016年9月20日9:34:00は42631.39861として保存されます。42631は、1900-01-01からの日数です。.39861は、真夜中から経過した時間の一部です。これを行うために10進数型を直接使用しないでください。明示的な日付/時刻型を使用してください。ここでの私のポイントは、単なる例示です。

2つの別々の列にデータを保存すると、特定の時点が保存された値よりも早いか遅いかを確認したいときに、両方の列の値を組み合わせる必要があります。

値を個別に保存すると、検出が困難な「バグ」が必ず発生します。たとえば、次のことを考えてください。

IF OBJECT_ID('tempdb..#DT') IS NOT NULL
DROP TABLE #DT;
CREATE TABLE #DT
(
    dt_value DATETIME NOT NULL
    , d_value DATE NOT NULL
    , t_value TIME(0) NOT NULL
);


DECLARE @d DATETIME = '2016-09-20 09:34:00';

INSERT INTO #DT (dt_value, d_value, t_value)
SELECT @d, CONVERT(DATE, @d), CONVERT(TIME(0), @d);

SET @d = '2016-09-20 11:34:00';

INSERT INTO #DT (dt_value, d_value, t_value)
SELECT @d, CONVERT(DATE, @d), CONVERT(TIME(0), @d);

/* show all rows with a date after 2016-07-01 11:00 am */
SELECT *
FROM #DT dt
WHERE dt.dt_value >= '2016-07-01 11:00:00';

/* show all rows with a date after 2016-07-01 11:00 am */
SELECT *
FROM #DT dt
WHERE dt.d_value >= CONVERT(DATE, '2016-07-01')
    AND dt.t_value >= CONVERT(TIME(0), '11:00:00');

上記のコードでは、テストテーブルを作成し、2つの値を設定してから、そのデータに対して簡単なクエリを実行しています。最初のSELECT行は両方の行を返しますが、2番目のSELECT行は単一の行のみを返します。これは望ましい結果ではない場合があります。

ここに画像の説明を入力してください

コメントで@ypercubeが指摘しているように、値が個別の列にある日付/時刻範囲をフィルタリングする正しい方法は次のとおりです。

WHERE dt.d_value > CONVERT(DATE, '2016-07-01') /* note there is no time component here */
    OR (
        dt.d_value = CONVERT(DATE, '2016-07-01') 
        AND dt.t_value >= CONVERT(TIME(0), '11:00:00')
    )

分析のために時間コンポーネントを分離する必要がある場合は、値の時間部分に計算された永続列を追加することを検討できます。

ALTER TABLE #DT
ADD dt_value_time AS CONVERT(TIME(0), dt_value) PERSISTED;

SELECT *
FROM #dt;

ここに画像の説明を入力してください

その後、永続化された列にインデックスを付けて、時刻ごとに高速ソートなどを行うことができます。

日付と時刻を表示のために2つのフィールドに分割することを検討している場合、フォーマットはサーバーではなくクライアントで実行する必要があることに注意してください。


11

私は他の答えに反対意見を提供するつもりです。

日付と時刻の両方のコンポーネントが一緒に必要な場合、つまり、一方に含まれるが他方には含まれない(または一方にNULLが含まれる)エントリが無効である場合、単一の列に格納することは、他の理由で意味があります答えます。

ただし、一方または両方のコンポーネントが個別にオプションである場合もあります。その場合、単一の列に格納するのは正しくありません。そうすると、任意の方法でNULL値を表すように強制されます。たとえば、時刻を00:00:00として保存します。

次に例を示します。

  • マイレージ税控除のために車両の旅を記録しています。旅行の正確な時間を知ることは有用ですが、従業員がそれを書き留めておらず、忘れてしまった場合でも、日付はそれ自体で記録する必要があります(必須の日付、オプションの時間)。

  • 人々が昼食を食べる時間を調べるために調査を実施しており、参加者に、日付を含む昼食時間のサンプルを記入してフォームに記入するように依頼します。日付の入力を気にしない人もいれば、データが本当に気になる時間(オプションの日付、必要な時間)なので、データを破棄したくありません。

代替アプローチについては、この関連する質問を参照してください。


ではRFC 3339「未知のオフセットローカル」を記録するための規則があります。「未知の時間」のユースケースをカバーしているとは思わないが、近い。次のセクション「現地時間」はさらに近いですが、それでも十分ではありません。
ジェネララマ

はい、私は今、このためにスキーマのリファクタリングの樽を見つめています。レンタカーの状況を見てください。レンタカー会社から車を拾うには、会社が開いている必要があります。そのため、ピックアップの日時を指定します。ただし、多くにはキードロップボックスがあります。時間外に降ります したがって、場所が日曜日に閉まっている場合、降車日があります。しかし、時間ではありません。0の値(午前12時など)の保存は、一部の場所は深夜まで開いているため機能しません。これは他の状況では有効な値です。
リース

5

特定のビジネス/アプリケーションの需要がない限り、常に単一の列として保存することを好みます。以下が私のポイントです-

  • タイムスタンプから時間を抽出することは問題ではありません
  • 両方を一緒に格納できる場合に、時間のために余分な列を追加する理由
  • クエリを実行するたびに日付と時刻が追加されるのを避けるため。

1
@a_horse_with_no_nameにはここにポイントがあります。私が考える「datetimestampから抽出タイムスタンプが問題ではありません」と言い換えるべきである「の抽出タイムスタンプから時間が問題ではありません」。「タイムスタンプ」は通常、日付と時刻(および通常はタイムゾーン)の両方を意味します。
ypercubeᵀᴹ

はい、@ypercubeᵀᴹに同意します。タイムスタンプは通常、日付と時刻の両方を意味します。DateTimeStampの単語に明示的に言及したので、だれでも日付と時刻の両方について話していることを理解できます。しかし、あなたも正しいです。答えを修正しました。
アシュウィニモハン

3

SQL Serverでは、DataTimeを1つのフィールドとして保存するのが最適です。DataTime列にインデックスを作成すると、Date検索およびDateTime検索として使用できます。したがって、特定の日付に存在するすべてのレコードを制限する必要がある場合でも、特別な操作を行わなくてもインデックスを使用できます。時間部分を照会する必要がある場合、同じインデックスを使用することはできません。したがって、DateTimeよりも時刻を重視するビジネスケースがある場合は、作成する必要があるため、個別に保存する必要があります。インデックスを作成し、パフォーマンスを改善します。


1

確かに、これは残念です。これには標準のクロスDBMSタイプはありません(INTやVARCHARは整数や文字列値用です)。これまでに出会った2つのクロスデータベースアプローチは、VARCHAR / CHAR列を使用してDataTime値をISO 8601(より便利で人間が読める)標準に従ってフォーマットされた文字列として保存し、BIGINTを使用してPOSIXタイムスタンプとして保存します(さらに保存されます)効率的、高速、簡単に数学的に操作できます)。


2
はいtimestamp。SQL標準で定義されていることです。タイムスタンプを文字列として保存することは非常に悪いアドバイスです
a_horse_with_no_name

0

たくさんのことを読んだ後、BIGINTのUTC Unix時間が最適なソリューションのようです。TZDBは、必要に応じてタイムゾーンストレージ用にVARCHAR内の1つのIDを倍にします。いくつかの引数:

  1. TIMESTAMPとDATETIMEは、バックグラウンドで大量の複雑な変換を行いますが、これは複雑で明確ではないようです。サーバーは、ローカル時間からUTCに、またはサーバー時間に切り替わり、また時々戻ります。すべての機能の隠れたオーバーヘッドの束。

  2. BIGINT(8kb)は、少なくともxxxxxx.xxxxxx形式のストレージに必要なDECIMALと同じかそれ以上です。これは、実際には2つのINT + MySQLによって保存されます。そして、何世紀も先に保存するのに十分です。

  3. ほとんどすべての主要なプログラミング言語には、Unix時間で動作する標準関数のライブラリがあります。

  4. BIGINTを使用した数学演算は、ハードウェア上で他のものよりもかなり高速または高速でなければなりません。

もちろん、上記のすべては、大規模な国際プロジェクトに関連しています。小さなものについては、選択したフレームワークのデフォルトのフォーマットを使用するだけで十分なようです。


2
バックグラウンドで大量の変換を実行しますが、それは明らかではないようです」-どのDBMSについて話しているのですか?timestamp列については(データベースレイヤーで) "ギミックコンバージョン"は発生せずtimestamp with time zone、マニュアル(少なくともOracleとPostgresの場合)で十分に文書化および説明されています
-a_horse_with_no_name

1
「ほとんどすべての主要なプログラミング言語には、Unix時間で動作する標準関数のライブラリがあります。」それでも、bigintを使用することを選択して、SQL / DBMSにある日付、日時、およびタイムスタンプに関するすべてのライブラリと関数を
破棄し
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.