OracleのDATEおよびINTERVALデータ型の内部動作について質問があります。 Oracle 11.2 SQLリファレンス によると、2つのDATEデータ型を減算すると、結果はNUMBERデータ型になります。
大まかなテストでは、これは本当のように見えます:
CREATE TABLE test (start_date DATE);
INSERT INTO test (start_date) VALUES (date'2004-08-08');
SELECT (SYSDATE - start_date) from test;
nUMBERデータ型を返します。
しかし、今あなたがするなら:
SELECT (SYSDATE - start_date) DAY(5) TO SECOND from test;
iNTERVALデータ型を取得します。つまり、OracleはDATE減算のNUMBERをINTERVAL型に変換できます。
だから今、私はブラケットに直接NUMBERデータ型を入れてみることができると考えました(とにかくNUMBERになる「SYSDATE-start_date」を行う代わりに):
SELECT (1242.12423) DAY(5) TO SECOND from test;
しかし、これはエラーになります:
ORA-30083: syntax error was found in interval value expression
だから私の質問は次のとおりです。ここで何が起こっているのでしょうか?日付を減算するとNUMBER(SELECTステートメント#1で示すように)になり、INTERVAL型(SELECTステートメント#3で示すように)に自動的にキャストできません。しかし、生のNUMBER(SELECTステートメント#2)を挿入する代わりにDATE減算式を使用すると、Oracleはなんとかできるようです。
ありがとう
OK、私は通常自分の質問には答えませんが、少し手を加えた後、OracleがDATE減算の結果をどのように保存するかを明確に把握しました。
2つの日付を減算すると、値はNUMBERデータ型ではありません( Oracle 11.2 SQLリファレンスマニュアル で信じられます)。 DATE減算の内部データ型番号は14です。これは、文書化されていない内部データ型です(NUMBERは 内部データ型番号2 です)。ただし、実際には2つの独立した2の補数の符号付き数値として保存され、最初の4バイトは日数を表し、最後の4バイトは秒数を表します。
正の整数差を生じるDATE減算の例:
select date '2009-08-07' - date '2008-08-08' from dual;
結果:
DATE'2009-08-07'-DATE'2008-08-08'
---------------------------------
364
select dump(date '2009-08-07' - date '2008-08-08') from dual;
DUMP(DATE'2009-08-07'-DATE'2008
-------------------------------
Typ=14 Len=8: 108,1,0,0,0,0,0,0
結果は、2つの別々の2の補数の符号付き4バイト数として表されることを思い出してください。この場合小数がないため(正確には364日と0時間)、最後の4バイトはすべて0であり、無視できます。 CPUはリトルエンディアンアーキテクチャを備えているため、最初の4バイトについては、バイトが逆になり、1,108または0x16c(10進数の364)として読み取る必要があります。
負の整数差を生じるDATE減算の例:
select date '1000-08-07' - date '2008-08-08' from dual;
結果:
DATE'1000-08-07'-DATE'2008-08-08'
---------------------------------
-368160
select dump(date '1000-08-07' - date '2008-08-08') from dual;
DUMP(DATE'1000-08-07'-DATE'2008-08-0
------------------------------------
Typ=14 Len=8: 224,97,250,255,0,0,0,0
繰り返しますが、私はリトルエンディアンのマシンを使用しているため、バイトは逆になり、255,250,97,224として読み取られる必要があります。これは11111111 11111010 01100001 11011111に対応します。左端の2進数が1であるため負です。これを10進数に変換するには、2の補数を逆にする(1を減算してから1の補数を行う)必要があります。結果は00000000 00000101 10011110 00100000です。
10進数の差を生じるDATE減算の例:
select to_date('08/AUG/2004 14:00:00', 'DD/MON/YYYY HH24:MI:SS'
- to_date('08/AUG/2004 8:00:00', 'DD/MON/YYYY HH24:MI:SS') from dual;
TO_DATE('08/AUG/200414:00:00','DD/MON/YYYYHH24:MI:SS')-TO_DATE('08/AUG/20048:00:
--------------------------------------------------------------------------------
.25
これら2つの日付の差は0.25日または6時間です。
select dump(to_date('08/AUG/2004 14:00:00', 'DD/MON/YYYY HH24:MI:SS')
- to_date('08/AUG/2004 8:00:00', 'DD/MON/YYYY HH24:MI:SS')) from dual;
DUMP(TO_DATE('08/AUG/200414:00:
-------------------------------
Typ=14 Len=8: 0,0,0,0,96,84,0,0
今度は、差が0日と6時間であるため、最初の4バイトは0であると予想されます。最後の4バイトについては、それらを逆にして(CPUがリトルエンディアンであるため)、84,96 = 01010100を取得できます01100000基数2 = 10進数で21600。 21600秒を時間に変換すると、6時間になりますが、これは予想した違いです。
これが、DATE減算が実際にどのように保存されるのか疑問に思っている人の助けになることを願っています。
日付の数学はNUMBERを返さないが、INTERVALを返すため、構文エラーが発生します。
SQL> SELECT DUMP(SYSDATE - start_date) from test;
DUMP(SYSDATE-START_DATE)
--------------------------------------
Typ=14 Len=8: 188,10,0,0,223,65,1,0
最初に NUMTODSINTERVAL関数 を使用して、例の数値を区間に変換する必要があります
例えば:
SQL> SELECT (SYSDATE - start_date) DAY(5) TO SECOND from test;
(SYSDATE-START_DATE)DAY(5)TOSECOND
----------------------------------
+02748 22:50:04.000000
SQL> SELECT (SYSDATE - start_date) from test;
(SYSDATE-START_DATE)
--------------------
2748.9515
SQL> select NUMTODSINTERVAL(2748.9515, 'day') from dual;
NUMTODSINTERVAL(2748.9515,'DAY')
--------------------------------
+000002748 22:50:09.600000000
SQL>
NUMTODSINTERVAL()関数を使用した逆キャストに基づいて、変換時に一部の丸めが失われるようです。
いくつかのポイント:
ある日付を別の日付から引くと、数値が得られます。あるタイムスタンプから別のタイムスタンプを引くと、間隔が生じます。
Oracleは、タイムスタンプ演算を実行するときに、タイムスタンプを内部で日付に変換します。
間隔定数は、日付またはタイムスタンプの算術演算では使用できません。
間隔値から時間/分/秒を取得するには、extract()関数を使用します。以下の例、2つのタイムスタンプ列から時間を取得する方法を参照してください。お役に立てれば!
iHB_INS_TS、MAIL_SENT_TS、extract((IHB_INS_TS-MAIL_SENT_TS)からの時間))IHB_ADJSMT_WKFL_NTFCTNからhourDiffを選択します。