7

Java と Oracle の両方にDate と呼ばれるタイムスタンプ型があります。開発者はこれらをカレンダーの日付であるかのように操作する傾向があり、これが厄介な 1 回限りのバグを引き起こすのを見てきました。

  1. 基本的な日付の数量については、入力時に時刻部分を単純に切り取ることができます。つまり、精度を下げます。ただし、日付範囲 (例: 9/29-9/30 ) でこれを行うと、これら 2 つの値の差は 2 日ではなく 1 日になります。また、範囲比較には、1) 切り捨て操作: start < trunc(now) <= end、または2) 算術: start < now < (end + 24hrs). ひどくはありませんが、DRYではありません。

  2. 別の方法として、9/ 29 00:00:00 - 10/1 00:00:00という実際のタイムスタンプを使用することもできます。(午前 0 時から午前 0 時までなので、10 月の一部は含まれません)。現在、期間は本質的に正しく、範囲の比較はより単純です: start <= now < end. 内部処理では確かにクリーンですが、終了日は最初の入力 (+1) で変換する必要があり、出力 (-1) では、ユーザー レベルでのカレンダー日付のメタファーを想定しています。

プロジェクトで日付範囲をどのように処理しますか? 他の選択肢はありますか?Java側とOracle側の両方でこれをどのように処理するかに特に興味があります.

4

11 に答える 11

7

これが私たちのやり方です。

  1. タイムスタンプを使用します。

  2. 比較のためにハーフオープン間隔を使用します start <= now < end

SQLを成功させるには、BETWEENがどういうわけか不可欠であると主張する囁き声を無視してください。

これにより、一連の日付範囲の監査が非常に簡単になります。のデータベース値9/30 to 10/1は1日(9/30)を含みます。次の間隔の開始は、前の間隔の終了と等しくなければなりません。このinterval[n-1].end == interval[n].startルールは監査に便利です。

start表示するときは、必要に応じて、フォーマットされたend-1を表示できます。結局のところ、「終わり」は実際にはルールがもはや真実ではなくなった最初の日であることを理解するように人々を教育することができます。したがって、「9/30から10/1」は、「9/30から有効、10/1から無効」を意味します。

于 2008-10-01T08:00:36.490 に答える
4

Oracle にはTIMESTAMP データ型があります。DATE データ型の年、月、日、および時、分、秒、小数秒の値が格納されます。

これは、日付計算に関するasktom.oracle.comのスレッドです。

于 2008-10-01T02:50:14.897 に答える
3

私は S.Lott が説明したことに二番目です。日付と時刻の範囲を広範に使用する製品スイートがあり、そのような範囲で作業することは、私たちが学んだ教訓の 1 つです。ちなみに、それがもはや範囲の一部ではない場合(IOW、半開間隔) 、終了日を排他的な終了日と呼びます。対照的に、時間部分がない場合にのみ意味をなす範囲の一部としてカウントされる場合、それは包括的な終了日です。

ユーザーは通常、包括的な日付範囲の入力/出力を期待しています。とにかく、ユーザー入力をできるだけ早く排他的な終了日付範囲に変換し、日付範囲をユーザーに表示する必要がある場合はできるだけ遅く変換してください。

データベースでは、常に排他的な終了日範囲を保存してください。終了日の範囲が含まれるレガシー データがある場合は、可能であればそれらを DB に移行するか、データが読み取られたらできるだけ早く終了日の範囲を含まないデータに変換します。

于 2008-11-15T23:40:45.010 に答える
2

私は Oracle の日付データ型を使用し、境界条件に影響を与える時間コンポーネントの問題について開発者を教育します。

データベース制約はまた、時間コンポーネントを持たないはずの列に誤って時間コンポーネントを指定することを防ぎ、時間コンポーネントを持つ値がないことをオプティマイザに伝えます。

たとえば、制約 CHECK (MY_DATE=TRUNC(MY_DATE)) は、時刻が 00:00:00 以外の値が my_date 列に配置されるのを防ぎ、Oracle が MY_DATE = TO_DATE(' 2008-09-12 15:00:00') は true にならないため、次のように展開できるため、テーブルから行が返されません。

MY_DATE = TO_DATE('2008-09-12 15:00:00') AND
TO_DATE('2008-09-12 15:00:00') = TRUNC(TO_DATE('2008-09-12 15:00:00'))

もちろん、これは自動的に false です。

日付を 20080915 のような数字として保存したくなることもありますが、これはクエリの最適化の問題を引き起こす可能性があります。たとえば、20,071,231 と 20,070,101 の間に有効な値はいくつありますか? 2007 年 12 月 31 日から 2008 年 1 月 1 日の間はどうでしょうか。また、20070100 などの不正な値を入力することもできます。

したがって、時間コンポーネントのない日付がある場合、範囲の定義は簡単になります。

select ...
from   ...
where  my_date Between date '2008-01-01' and date '2008-01-05'

時間コンポーネントがある場合、次のいずれかを実行できます。

select ...
from   ...
where  my_date >= date '2008-01-01' and
       my_date  < date '2008-01-06'

また

select ...
from   ...
where  my_date Between date '2008-01-01'
                   and date '2008-01-05'-(1/24/60/60)

マジック ナンバーの代わりに (1/24/60/60) を使用していることに注意してください。Oracle では、定義された 1 日の端数を加算して日付計算を実行するのが一般的です... 3/24 は 3 時間、27/24/60 は 27 分です。このタイプの Or​​acle の計算は正確であり、丸め誤差が発生しないため、次のようになります。

select 27/24/60 from dual;

... 0.01874999999999 などではなく、0.01875 を返します。

于 2008-10-01T02:48:38.863 に答える
1

Interval データ型がまだ投稿されていません。

Oracleには、正確なシナリオのデータ型もあります。Oracle にも INTERVAL YEAR TO MONTH および INTERVAL DAY TO SECOND データ型があります。

10gR2 ドキュメントから。

INTERVAL YEAR TO MONTH は、YEAR および MONTH 日時フィールドを使用して期間を格納します。このデータ型は、年と月の値のみが重要な場合に、2 つの日時値の差を表すのに役立ちます。

INTERVAL YEAR [(year_precision)] TO MONTH

ここで、 year_precision は YEAR 日時フィールドの桁数です。year_precision のデフォルト値は 2 です。

INTERVAL DAY TO SECOND データ型

INTERVAL DAY TO SECOND は、期間を日、時間、分、および秒で格納します。このデータ型は、2 つの日時値の正確な差を表すのに役立ちます。

このデータ型を次のように指定します。

INTERVAL DAY [(day_precision)] TO SECOND [(fractional_seconds_precision)]

どこ

day_precision は、DAY 日時フィールドの桁数です。許容値は 0 ~ 9 です。デフォルトは 2 です。

fractional_seconds_precision は、SECOND 日時フィールドの小数部分の桁数です。許容値は 0 ~ 9 です。デフォルトは 6 です。

間隔値をリテラルとして指定する場合、非常に柔軟に対応できます。間隔値をリテラルとして指定する方法の詳細については、「間隔リテラル」を参照してください。間隔の使用例については、「日時と間隔の例」も参照してください。

于 2008-10-01T15:26:29.120 に答える
0

getTime()の結果を長整数として格納することにより、すべての日付を GMT タイムスタンプとして明確に格納できます (つまり、タイムゾーンや夏時間の問題はありません)。

データベース クエリで日、週、月などの操作が必要な場合、およびクエリのパフォーマンスが最も重要な場合、タイムスタンプ (ミリ秒よりも高い粒度に正規化) を、その日の列を持つ日付内訳テーブルにリンクできます。 、週、月などの値を使用して、コストのかかる日付/時刻関数をクエリで使用する必要がないようにします。

于 2008-10-01T03:48:34.350 に答える
0

Alan の言うとおりです。Joda の時間はすばらしいものです。java.util.Date と Calendar は残念です。

タイムスタンプが必要な場合は、時間とともにオラクルの日付型を使用し、列に_tmstなどのサフィックスを付けて名前を付けます。データをJavaに読み込むと、それをjoda time DateTimeオブジェクトに取得します。タイムゾーンが正しいことを確認するには、オラクルにタイムゾーンとともにタイムスタンプを格納する特定のデータ型があることを考慮してください。または、テーブルに別の列を作成して、タイムゾーン ID を格納することもできます。タイムゾーン ID の値は、タイムゾーンの標準のフル ネーム ID である必要があります。29 . TZ dta に別の列を使用する場合は、データを Java に読み込むときに DateTime オブジェクトを使用しますが、.withZoneRetainFields を使用して DateTime オブジェクトにタイムゾーンを設定し、タイムゾーンを設定します。

日付データ (タイムスタンプなし) のみが必要な場合は、データベースで日付型を時間なしで使用します。もう一度よく名前を付けます。この場合、jodatime の DateMidnight オブジェクトを使用します。

結論: データベースの型システムと使用している言語を活用します。それらを学び、表現力豊かな API と言語構文を使用して問題に対処するメリットを享受してください。

于 2008-10-01T04:19:30.423 に答える
0

最初の文に基づいて、Java の隠された「機能」(つまりバグ) の 1 つに出くわしてjava.util.Dateいます。(Java 7 では、新しい日付/時刻 API でこれを修正することが約束されています。) ほとんどすべてのエンタープライズ アプリはさまざまな時間パターンに依存しており、ある時点で日付と時刻の計算を行う必要があります。

理想的には、Google カレンダーで使用されるJoda timeを使用できます。これができない場合は、java.util.DateGrails/Rails に似た計算方法を備えたラッパーと、ラッパーの範囲 (つまり、期間の開始と終了を示す順序付けられたペア)で構成される API を推測します。十分であること。

私の現在のプロジェクト (HR タイムキーピング アプリケーション) では、すべての日付を Oracle と Java の両方で同じタイムゾーンに正規化しようとしています。幸いなことに、ローカリゼーション要件は軽量です (= 1 つのタイムゾーンで十分です)。永続オブジェクトが 1 日より細かい精度を必要としない場合は、午前 0 時のタイムスタンプを使用します。さらに進んで、余分なミリ秒を破棄して、永続オブジェクトが許容できる最も粗い粒度にすることを主張します (処理が簡単になります)。

于 2008-10-01T02:47:27.603 に答える
0

私の経験に基づいて、それを行うには主に4つの方法があります。

1) 日付をエポック整数 (1970 年 1 月 1 日からの秒数) に変換し、整数としてデータベースに格納します。

2) 日付を YYYYMMDDHHMMSS 整数に変換し、整数としてデータベースに格納します。

3) 日付として保存する

4) 文字列として保存する

私は常に 1 と 2 に固執してきました。これにより、日付を使用してすばやく簡単な計算を実行でき、基礎となるデータベース機能に依存する必要がないからです。

于 2008-10-01T02:48:49.610 に答える
-1

すべての日付をミリ秒単位で保存しています。タイムスタンプ/日時フィールドはまったく使用しません。

だから、私はそれを長い間操作しなければなりません。これは、SQL クエリで「before」、「after」、「now」キーワードを使用しないことを意味します。

于 2008-10-01T02:53:04.717 に答える