web-dev-qa-db-ja.com

データベースに時間(hh:mm)を保存する最良の方法

データベーステーブルに時間を保存したいのですが、時間と分のみを保存する必要があります。 DATETIMEを使用して日付の他のコンポーネントを無視できることはわかっていますが、実際に必要な情報よりも多くの情報を保存せずにこれを行う最善の方法は何ですか?

91
Matthew Dresser

真夜中を過ぎた分数の整数として保存できます:

例えば。

0 = 00:00 
60 = 01:00
252 = 04:12

ただし、時間を再構成するにはいくつかのコードを記述する必要がありますが、それは難しいことではありません。

120
BigJump

SQL Server 2008+を使用している場合は、 TIME データ型を検討してください。 SQLTeamの記事 使用例の追加。

51
WebMatrix

DATETIME開始DATETIME終了

代わりに2つのDATETIME値を使用して、event_startおよびevent_end

時間は複雑なビジネスです

現在、ほとんどの測定では、ほとんどまたはすべての測定に、デナリーベースのメトリックシステムが採用されています。少なくとも、gはmlであり、cmは立方cmであることに全員が同意できるため、これは全体的に良好です。少なくともほぼそうです。メトリックシステムには多くの欠陥がありますが、少なくとも国際的に一貫して欠陥があります。

しかし、時間が経つにつれて、我々は持っています。毎秒1000ミリ秒、60秒から1分、60分から1時間、半日ごとに12時間、問題の月や年によっても異なる月あたり約30日。 、各国での時刻のフォーマット方法は異なります。

消化することはたくさんありますが、そのような複雑なシナリオで単純な解決策をとることは不可能です。

一部のコーナーはカットできますが、そうでない方が賢明なコーナーがあります

ここでの一番の答えは、真夜中を過ぎた分の整数を保存することは完全に合理的であると思われることを示唆していますが、そうするのを避けることを学びました。

2つのDATETIME値を実装する理由は、精度、解像度、およびフィードバックを向上させるためです。

これらはすべて、設計で望ましくない結果が生じる場合に非常に便利です。

必要以上のデータを保存していますか?

最初は必要以上に多くの情報が保存されているように見えるかもしれませんが、このヒットを取得する正当な理由があります。

この余分な情報を保存することで、長期的には時間と労力を節約できます。何かがかかった時間を誰かに伝えると、イベントがいつどこで発生したかを知りたくなります。

それは巨大な惑星です

過去に、私は自分自身以外にこの惑星に他の国が存在することを無視する罪を犯してきました。当時は良いアイデアのように思えましたが、これは常に問題、頭痛、そして後の無駄な時間をもたらしました。常にすべてのタイムゾーンを考慮してください。

C#

DateTimeは、C#の文字列に適切にレンダリングされます。 ToString(string Format)メソ​​ッドはコンパクトで読みやすいです。

例えば。

new TimeSpan(EventStart.Ticks - EventEnd.Ticks).ToString("h'h 'm'm 's's'")

SQLサーバー

また、データベースをアプリケーションインターフェイスとは別に読んでいる場合、dateTimesは一目で読むことができ、計算を実行するのは簡単です。

例えば。

SELECT DATEDIFF(MINUTE, event_start, event_end)

ISO8601日付標準

SQLiteを使用している場合、これはありません。そのため、代わりにテキストフィールドを使用してISO8601形式で保存します。

「2013-01-27T12:30:00 + 0000」

ノート:

  • これは24時間時計を使用します*

  • ISO8601のタイムオフセット(または+0000)部分は、GPSコーディエートの経度値に直接マップされます(夏時間または国全体を考慮していません)。

例えば。

TimeOffset=(±Longitude.24)/360 

...ここで、±は東または西の方向を指します。

したがって、経度、緯度、高度をデータとともに保存する価値があるかどうかを検討する価値があります。これはアプリケーションによって異なります。

  • ISO8601は国際的な形式です。

  • このウィキは、詳細については http://en.wikipedia.org/wiki/ISO_8601 で非常に役立ちます。

  • 日付と時刻は国際時間で保存され、オフセットは世界のどこに保存されているかに応じて記録されます。

私の経験では、プロジェクトを開始するときにあると思うかどうかに関係なく、常に完全な日付と時刻を保存する必要があります。 ISO8601は、非常に優れた将来性のある方法です。

無料の追加アドバイス

また、イベントをチェーンのようにグループ化する価値があります。例えば。レースを記録する場合、イベント全体をレーサー、race_circuit、circuit_checkpoints、circuit_lapsでグループ化できます。

私の経験では、誰がレコードを保存したかを特定することも賢明です。トリガーを介して入力された別のテーブルとして、または元のテーブル内の追加の列として。

入れれば入れるほど出て行きます

できる限りスペースを節約して経済的になりたいという願望は完全に理解していますが、情報を失うという犠牲を払うことはめったにありません。

データベースに関する経験則は、タイトルが示すように、データベースはデータがある量だけを伝えることができ、履歴データを遡ってギャップを埋めることは非常にコストがかかる可能性があります。

解決策は、最初に正しく取得することです。これは確かに言うよりも簡単ですが、効果的なデータベース設計のより深い洞察が得られ、その後、最初に正しくなる可能性が大幅に向上するはずです。

初期設計が優れているほど、修理のコストは低くなります。

私がこれをすべて言っているのは、もし私が時間内に戻ることができれば、そこに着いたときにそれが自分に言うことだからです。

11

通常の日時を保存し、それ以外はすべて無視します。日時をロードするだけで、intをロードし、操作し、日時に変換するコードを書くのに余分な時間を費やすのはなぜですか?

9
Seth

sQL Server 2008を使用している場合は少し言及していなかったため、timeデータ型を使用できます。

4
SQLMenace

SQL Serverは、実際には時間を1日の端数として保存します。たとえば、1日間= 1の値。12時間は0.5の値です。

DATETIME型を使用せずに時刻値を格納する場合は、10進数形式で時刻を格納する必要がありますが、DATETIMEへの変換も簡単です。

例えば:

SELECT CAST(0.5 AS DATETIME)
--1900-01-01 12:00:00.000

値をDECIMAL(9,9)として保存すると、5バイトを消費します。ただし、精度が最も重要でない場合、REALは4バイトしか消費しません。どちらの場合でも、集計計算(平均時間)は数値で簡単に計算できますが、データ/時間型では計算できません。

3
Taylor Gerring

それらを整数(HH * 3600 + MM * 60)に変換し、そのように保存します。小さいストレージサイズでありながら、操作が簡単です。

2
Glen Solsberry

MySQLを使用している場合は、TIMEのフィールドタイプと、TIMEに付属する関連機能を使用します。

00:00:00は標準のUNIX時間形式です。

手でテーブルを振り返って確認する必要がある場合、整数は実際のタイムスタンプよりも混乱する可能性があります。

2
Syntax

真夜中の数分前ではなく、24時間時計としてSMALLINTとして保存します。

09:12 = 912 14:15 = 1415

「人間が読み取れる形式」に戻す場合、コロン「:」を右から2文字挿入するだけです。必要に応じて、ゼロで左パッドします。数学をそれぞれの方法で保存し、使用するバイト数を(varcharに比べて)数バイト少なくし、さらに値が(英数字ではなく)数値であることを強制します

かなりばかげています... MS SQLには既に1年もの間TIMEデータ型があったはずです...

1
Kristen

あなたが求めているのは、分として数値を格納する変数です。これは、さまざまなタイプの整数変数を使用して実行できます。

SELECT 9823754987598 AS MinutesInput

次に、プログラムで次のように計算することにより、希望する形式でこれを表示できます。

long MinutesInAnHour = 60;

long MinutesInADay = MinutesInAnHour * 24;

long MinutesInAWeek = MinutesInADay * 7;


long MinutesCalc = long.Parse(rdr["MinutesInput"].toString()); //BigInt converts to long. rdr is an SqlDataReader.   


long Weeks = MinutesCalc / MinutesInAWeek;

MinutesCalc -= Weeks * MinutesInAWeek;


long Days = MinutesCalc / MinutesInADay;

MinutesCalc -= Days * MinutesInADay;


long Hours = MinutesCalc / MinutesInAnHour;

MinutesCalc -= Hours * MinutesInAnHour;


long Minutes = MinutesCalc;

効率の使用を要求する場所で問題が発生します。ただし、時間が足りない場合は、null許容のBigIntを使用して分値を保存するだけです。

値nullは、時間がまだ記録されていないことを意味します。

次に、宇宙空間への往復の形で説明します。

残念ながら、テーブル列には単一の型のみが格納されます。したがって、必要に応じて、タイプごとに新しいテーブルを作成する必要があります。

例えば:

  • MinutesInput = ..255の場合、TinyIntを使用します(上記のように変換します)。

  • MinutesInput = 256 ..131071の場合、SmallIntを使用します(注:SmallIntの最小値は-32,768です。したがって、格納するときは32768を否定して追加します上記のように変換する前に、全範囲を利用するための値を取得します)。

  • MinutesInput = 131072 ..8589934591の場合、Intを使用します(注:必要に応じて2147483648を無効にして追加します)。

  • MinutesInput = 8589934592 ..6893488147419103231の場合、BigIntを使用します(注:必要に応じて9223372036854775808を追加および無効にします)。

  • MinutesInput> 6893488147419103231の場合、charは1バイトであるため、必要に応じてXを増やすVARCHAR(X)を個人的に使用します。後でこの回答を再確認して、これを完全に説明する必要があります(または、他のStackoverfloweeがこの回答を終了できる場合があります)。

各値には間違いなく一意のキーが必要なので、データベースの効率は、格納されている値の範囲が非常に小さい(0分に近い)と非常に高い(8589934591より大きい)の適切な組み合わせである場合にのみ明らかになります。

格納されている値が実際に36893488147419103231を超える数値に達するまで、分を表す単一のBigInt列を使用することもできます。これは、一意の識別子と別のintを使用して分値を格納する必要がないためです。

1

Smalldatetimeを試してください。それはあなたが望むものを与えないかもしれませんが、それはあなたの日付/時刻操作におけるあなたの将来のニーズであなたを助けます。

1
MarlonRibunal

本当に必要なのは時間と分だけですか?タイムゾーンとDSTに関する情報がないと、(たとえば、このような2つのデータポイント間のタイムスパンを計算するなど)意味のある何かをしたい場合、誤った結果が得られる可能性があります。あなたの場合、タイムゾーンはおそらく適用されませんが、DSTは最も確実に適用されます。

1
mghie

Kristenが示唆したように、UTC形式での時間の節約はより良い助けになります。

子午線AMまたはPMはUTCで使用されないため、24時間クロックを使用していることを確認してください。

例:

  • 4:12-0412
  • 10:12 AM-1012
  • 2:28 PM-1428
  • 11:56 PM-2356

標準の4桁形式を使用することをお勧めします。

0
balamurugavel

tickslong/bigintとして保存します。これは現在ミリ秒単位で測定されています。更新された値は、TimeSpan.TicksPerSecond値を調べることで見つけることができます。

ほとんどのデータベースには、バックグラウンドでティックとして時刻を自動的に保存するDateTimeタイプがありますが、一部のデータベースの場合、たとえばティックを保存するSqlLiteは、日付を保存する方法です。

ほとんどの言語では、TicksTimeSpanTicksから簡単に変換できます。

C#では、コードは次のようになります。

long TimeAsTicks = TimeAsTimeSpan.Ticks;

TimeAsTimeSpan = TimeSpan.FromTicks(TimeAsTicks);

ただし、SqlLiteの場合、少数の異なるタイプしか提供されないため、注意してください。 INTREALおよびVARCHARティックの数を文字列または2つのINTセルを組み合わせて格納する必要があります。これは、INTが32ビットの符号付き数値であるのに対し、BIGINTは64ビットの符号付き数値であるためです。

しかし、私の個人的な好みは、日付と時刻をISO8601文字列として保存することです。

0