it-swarm.com.de

Subtrahieren von Daten in Oracle - Zahlen- oder Intervalldatentyp?

Ich habe eine Frage zu einigen internen Funktionen für die Oracle-Datentypen DATE und INTERVAL. Gemäß Oracle 11.2 SQL Reference ist das Ergebnis, wenn Sie 2 DATE-Datentypen subtrahieren, ein NUMBER-Datentyp.

Bei flüchtigen Tests scheint dies zu stimmen:

CREATE TABLE test (start_date DATE);
INSERT INTO test (start_date) VALUES (date'2004-08-08');
SELECT (SYSDATE - start_date) from test;

gibt einen NUMBER-Datentyp zurück.

Aber jetzt, wenn Sie es tun:

SELECT (SYSDATE - start_date) DAY(5) TO SECOND from test;

sie erhalten einen INTERVAL-Datentyp. Mit anderen Worten, Oracle kann die NUMBER von der DATE-Subtraktion in einen INTERVAL-Typ konvertieren.

Also dachte ich mir, ich könnte versuchen, einen NUMBER-Datentyp direkt in die Klammern einzufügen (anstatt 'SYSDATE - start_date', was sowieso eine NUMBER ergibt):

SELECT (1242.12423) DAY(5) TO SECOND from test;

Dies führt jedoch zu dem Fehler:

ORA-30083: syntax error was found in interval value expression

Meine Frage lautet also: Was ist hier los? Es scheint, dass das Subtrahieren von Datumsangaben zu einer NUMBER führen sollte (wie in SELECT-Anweisung 1 gezeigt), die NICHT automatisch in den Typ INTERVAL umgewandelt werden kann (wie in SELECT-Anweisung 3 gezeigt). Aber Oracle scheint in der Lage zu sein, dies irgendwie zu tun, wenn Sie den DATE-Subtraktionsausdruck verwenden, anstatt eine rohe NUMBER (SELECT-Anweisung # 2) einzugeben.

Vielen Dank

21
BYS2

Ok, ich beantworte normalerweise nicht meine eigenen Fragen, aber nach einigem Basteln habe ich definitiv herausgefunden, wie Oracle das Ergebnis einer DATE-Subtraktion speichert.

Wenn Sie 2 Daten subtrahieren, ist der Wert kein NUMBER-Datentyp (wie im Oracle 11.2 SQL-Referenzhandbuch angenommen). Die interne Datentypnummer einer DATE-Subtraktion ist 14, was ein nicht dokumentierter interner Datentyp ist (NUMBER ist interne Datentypnummer 2 ). Es wird jedoch tatsächlich als 2 separate vorzeichenbehaftete Zweierzahlen gespeichert, wobei die ersten 4 Bytes für die Anzahl der Tage und die letzten 4 Bytes für die Anzahl der Sekunden verwendet werden.

Ein Beispiel für eine DATE-Subtraktion, die zu einer positiven Ganzzahldifferenz führt:

select date '2009-08-07' - date '2008-08-08' from dual;

Ergebnisse in:

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

Erinnern Sie sich daran, dass das Ergebnis als 2 separate Zweierkomplement-4-Byte-Zahlen dargestellt wird. Da es in diesem Fall keine Dezimalstellen gibt (genau 364 Tage und 0 Stunden), sind die letzten 4 Bytes alle Nullen und können ignoriert werden. Für die ersten 4 Bytes sind die Bytes umgekehrt, da meine CPU eine Little-Endian-Architektur hat, und sollten als 1.108 oder 0x16c gelesen werden, was dezimal 364 ist.

Ein Beispiel für eine DATE-Subtraktion, die zu einer negativen Ganzzahldifferenz führt:

select date '1000-08-07' - date '2008-08-08' from dual;

Ergebnisse in:

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

Da ich eine Little-Endian-Maschine verwende, sind die Bytes umgekehrt und sollten als 255,250,97,224 gelesen werden, was 11111111 11111010 01100001 11011111 entspricht. Da dies nun eine binäre Zahlencodierung mit zwei Komplementen ist, wissen wir, dass die Zahl ist negativ, da die Binärziffer ganz links eine 1 ist. Um dies in eine Dezimalzahl umzuwandeln, müssten wir das Zweierkomplement umkehren (1 subtrahieren und dann das Einerkomplement ausführen).

Ein Beispiel für eine DATE-Subtraktion, die zu einer Dezimaldifferenz führt:

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

Die Differenz zwischen diesen beiden Daten beträgt 0,25 Tage oder 6 Stunden.

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

Da diesmal die Differenz 0 Tage und 6 Stunden beträgt, wird erwartet, dass die ersten 4 Bytes 0 sind. Für die letzten 4 Bytes können wir sie umkehren (da die CPU Little-Endian ist) und 84,96 = 01010100 erhalten 01100000 Basis 2 = 21600 in Dezimalzahl. Wenn Sie 21600 Sekunden in Stunden umrechnen, erhalten Sie 6 Stunden. Dies ist der erwartete Unterschied.

Ich hoffe, dies hilft allen, die sich gefragt haben, wie eine DATUMS-Subtraktion tatsächlich gespeichert wird.

33
BYS2

Sie erhalten den Syntaxfehler, weil die Datumsmathematik keine NUMBER, sondern ein INTERVAL zurückgibt:

SQL> SELECT DUMP(SYSDATE - start_date) from test;

DUMP(SYSDATE-START_DATE)
-------------------------------------- 
Typ=14 Len=8: 188,10,0,0,223,65,1,0

Sie müssen die Zahl in Ihrem Beispiel zuerst mit der Funktion NUMTODSINTERVAL in ein INTERVALL konvertieren

Beispielsweise:

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>

Basierend auf der umgekehrten Umwandlung mit der Funktion NUMTODSINTERVAL () scheint es, dass bei der Übersetzung etwas Rundung verloren geht.

6
tawman

Ein paar Punkte:

  • Das Subtrahieren eines Datums von einem anderen ergibt eine Zahl; Das Subtrahieren eines Zeitstempels von einem anderen führt zu einem Intervall.

  • Oracle konvertiert Zeitstempel intern in Datumsangaben, wenn Zeitstempel-Arithmetik ausgeführt wird.

  • Intervallkonstanten können weder in Datums- noch in Zeitstempelberechnungen verwendet werden.

Oracle 11gR2 SQL Referenz Datetime Matrix

1

Verwenden Sie die Funktion extract (), um Stunde/Minute/Sekunde vom Intervallwert abzurufen. Im folgenden Beispiel wird gezeigt, wie Stunden aus zwei Zeitstempelspalten abgerufen werden. Hoffe das hilft!

wähle IHB_INS_TS, MAIL_SENT_TS, extrahiere (Stunde von (IHB_INS_TS - MAIL_SENT_TS)) hourDiff von IHB_ADJSMT_WKFL_NTFCTN;

0
Sandeep Raul