2012-02-17 25 views
17

Mam pytanie dotyczące niektórych wewnętrznych operacji dla typów danych Oracle DATE i INTERVAL. Zgodnie z Oracle 11.2 SQL Reference, gdy odejmiesz 2 DATOWE typy danych, wynikiem będzie NUMBER typ danych.Odejmowanie dat w Oracle - Number lub Interval Typ danych?

Z pobieżnego badania, to wydaje się być prawdą:

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

zwróci typ danych numerycznych.

Ale teraz, jeśli nie:

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

masz typ danych interwału. Innymi słowy, Oracle może przekonwertować NUMBER z odejmowania DATE na typ INTERVAL.

Więc teraz pomyślałem, mogłem spróbować umieścić w typ danych numer bezpośrednio w nawiasach (zamiast robić „sysdate - START_DATE”, co powoduje w wielu anyways):

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

Ale to prowadzi do błędu :

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

Moje pytanie brzmi: co się tutaj dzieje? Wygląda na to, że odejmowanie dat powinno prowadzić do LICZBY (jak pokazano w instrukcji SELECT # 1), która NIE MOŻE być automatycznie rzutowana na typ INTERVAL (jak pokazano w instrukcji SELECT # 3). Ale Oracle wydaje się być w stanie to zrobić w jakiś sposób, jeśli użyjesz wyrażenia odejmowania DATE zamiast wstawiania surowego NUMBER (instrukcja SELECT # 2).

Dzięki

+0

Czy zdarza się to kiedy można umieścić zamiast sysdate coś jak TO_DATE (' 20120216' , 'rrrrmmdd')? –

+0

Cześć przepraszam, nie widziałem twojego komentarza do tej pory: S .. Po pierwsze, aby odpowiedzieć na twoje pytanie, tak, robi to nawet jeśli używa "to_date()" zamiast SYSDATE. Z drugiej strony udało mi się dowiedzieć, jak zapisuje się odejmowanie DATE, więc napisałem to jako moją odpowiedź: D .. ponieważ nikt inny w Internecie nie napisał o tym jeszcze, pomyślałem, że napiszę co ja dowiedziałem się. – BYS2

+0

@ BYS2 - Po co wstawiać liczbę(), a następnie spróbować odjąć dni od liczby i przekonwertować ją na sekundy: WYBIERZ (1242.12423) DZIEŃ (5) DO DRUGIEGO od testu? Oto, o co musisz zapytać. – Art

Odpowiedz

27

Ok, normalnie nie odpowiadam na własne pytania, ale po odrobinie majsterkowania, ustaliłem definitywnie, w jaki sposób Oracle przechowuje wynik odejmowania DATE.

Po odjęciu 2 dat, wartość nie jest typem danych LICZBY (tak, jak uważasz, że Oracle 11.2 SQL Reference manual). Wewnętrzny numer typu danych odejmowania DATE to 14, który jest nieudokumentowanym wewnętrznym typem danych (NUMBER to internal datatype number 2). Jest on jednak przechowywany jako 2 oddzielne podpisane dopełnienia dwóch uzupełnień, przy czym pierwsze 4 bajty służą do reprezentowania liczby dni i ostatnich 4 bajtów używanych do reprezentowania liczby sekund.

Przykład odejmowania DATE, w wyniku dodatniej różnicy całkowitej:

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

Wyniki w:

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 

Przypomnijmy, że wynik jest przedstawiony jako 2 oddzielne uzupełnienie dwójkowe podpisany 4 liczby bajtów . Ponieważ w tym przypadku nie ma liczb dziesiętnych (dokładnie 364 dni i 0 godzin), ostatnie 4 bajty są zerami i można je zignorować. W przypadku pierwszych 4 bajtów, ponieważ mój procesor ma architekturę typu little-endian, bajty są odwrócone i powinny być odczytywane jako 1,108 lub 0x16c, który jest dziesiętny 364.

Przykład odejmowania DATE, co daje ujemną różnicę całkowitą :

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

Wyniki w:

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 

Ponownie, ponieważ używam mało endian maszynę, bajty są odwracane i powinny być odczytywane jako 255,250,97,224 która odpowiada 11111111 11111010 01100001 11011111. Teraz, ponieważ jest to dopełnienie dwójki podpisane kodowanie liczb binarnych, wiemy, że liczba jest ujemna, ponieważ najdalsza od lewej cyfra binarna to 1. Aby przekonwertować to na liczbę dziesiętną, musielibyśmy odwrócić dopełnienie 2 (odjąć 1, a następnie uzupełnić), uzyskując: 00000000 00000101 10011110 00100000, co odpowiada -368160 jako podejrzane.

Przykład odejmowania DATE, w wyniku różnicy dziesiętnych:

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 

Różnica między tymi 2 dniach jest 0,25 dzień lub około 6 godzin.

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 

Teraz ten czas, ponieważ różnica wynosi 0 dni i 6 godzin, oczekuje się, że pierwsze 4 bajty są 0. Przez ostatnie 4 bajty, możemy je odwrócić (bo procesor jest little-endian) i uzyskaj 84,96 = 01010100 01100000 podstawa 2 = 21600 w systemie dziesiętnym. Konwersja 21600 sekund na godziny daje 6 godzin, co jest różnicą, której się spodziewaliśmy.

Mam nadzieję, że pomoże to każdemu, kto zastanawia się, jak faktycznie odejmowanie DATE jest rzeczywiście przechowywane.

5

Otrzymasz błąd składni, ponieważ data matematyka nie zwraca numer, ale zwraca przedziale:

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

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

trzeba konwertować liczby w Twojej przykład w odstępie pierwszy stosując NUMTODSINTERVAL Function

np

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> 

Na podstawie odwrotnego rzutowania za pomocą funkcji NUMTODSINTERVAL(), wydaje się, że w tłumaczeniu brakuje częściowego zaokrąglenia.

+0

Ah, dodatkowe 5 sekund to prawdopodobnie zegar ścienny, ponieważ SYSDATE był punktem odniesienia. Zaryzykowałbym przypuszczenie, że nie było zaokrąglenia. – tawman

+0

hmm dzięki za odpowiedź, ale to jest dziwne, ponieważ funkcja dump() zwróciła typ jako "14". Ale INTERVAL YEAR TO MONTH to typ 182, a DAY TO SECOND to typ 183 (http://docs.oracle.com/cd/B10500_01/appdev.920/a96584/oci03typ.htm#421773) ... Ponadto, jeśli data odejmuje naprawdę zwraca interwał, to dlaczego SQL * Plus wyświetla "2748.9515" po odjęciu dat? i dlaczego oficjalny podręcznik referencyjny podaje odjęcie daty w LICZBIE? – BYS2

+0

Odejmowanie DATE najwyraźniej daje efekt FLOAT. (Ktoś ma jakieś naprawdę stare dokumenty Oracle, które wskazują, że mogło to być kiedyś?) Wypróbuj 'SELECT * FROM DBA_TYPES WHERE TYPE_OID = '0000000000000000000000000000000E''. Spójrz również na pakiet SYS.STANDARD, gdzie wygląda na to, że FLOAT jest faktycznie taki sam jak NUMBER (tj. "Podtyp FLOAT to NUMBER"). Udostępnij i ciesz się. –

1

Kilka punktów:

  • odejmując jedną datę od innej wyników w szeregu; odjęcie jednego znacznika czasu od innego skutkuje interwałem.

  • Oracle konwertuje znaczniki czasu do dat wewnętrznie podczas wykonywania arytmetycznych znaczników czasu.

  • Stałe interwałowe nie mogą być używane w arytmetyce dat ani datownika.

Oracle 11gR2 SQL Reference Datetime Matrix

+0

Hej dziękuję za twój wkład, ale widzę moją odpowiedź ... Dokumentacja Oracle jest w rzeczywistości niepoprawna/niekompletna w tym przypadku – BYS2

-3

wybrać TIMEDIFF (STR_TO_DATE ("19:15,% H:% i% P '), STR_TO_DATE (" 09:58,% H:% i% p '))

+1

To nie próbuje odpowiedzieć na pytanie i nie jest poprawną składnią dla Oracle; brakowało Ci tagów Oracle i referencji w tytule i pytaniu? –