Subtracting Dates in Oracle - Number or Interval Datatype?

后端 未结 5 1514
小鲜肉
小鲜肉 2020-12-02 23:21

I have a question about some of the internal workings for the Oracle DATE and INTERVAL datatypes. According to the Oracle 11.2 SQL Reference, when you subtract 2 DATE dataty

相关标签:
5条回答
  • 2020-12-02 23:58

    select TIMEDIFF (STR_TO_DATE('07:15 PM', '%h:%i %p') , STR_TO_DATE('9:58 AM', '%h:%i %p'))

    0 讨论(0)
  • 2020-12-02 23:59

    You get the syntax error because the date math does not return a NUMBER, but it returns an 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
    

    You need to convert the number in your example into an INTERVAL first using the NUMTODSINTERVAL Function

    For example:

    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>
    

    Based on the reverse cast with the NUMTODSINTERVAL() function, it appears some rounding is lost in translation.

    0 讨论(0)
  • 2020-12-03 00:05

    Ok, I don't normally answer my own questions but after a bit of tinkering, I have figured out definitively how Oracle stores the result of a DATE subtraction.

    When you subtract 2 dates, the value is not a NUMBER datatype (as the Oracle 11.2 SQL Reference manual would have you believe). The internal datatype number of a DATE subtraction is 14, which is a non-documented internal datatype (NUMBER is internal datatype number 2). However, it is actually stored as 2 separate two's complement signed numbers, with the first 4 bytes used to represent the number of days and the last 4 bytes used to represent the number of seconds.

    An example of a DATE subtraction resulting in a positive integer difference:

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

    Results 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
    

    Recall that the result is represented as a 2 seperate two's complement signed 4 byte numbers. Since there are no decimals in this case (364 days and 0 hours exactly), the last 4 bytes are all 0s and can be ignored. For the first 4 bytes, because my CPU has a little-endian architecture, the bytes are reversed and should be read as 1,108 or 0x16c, which is decimal 364.

    An example of a DATE subtraction resulting in a negative integer difference:

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

    Results 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
    

    Again, since I am using a little-endian machine, the bytes are reversed and should be read as 255,250,97,224 which corresponds to 11111111 11111010 01100001 11011111. Now since this is in two's complement signed binary numeral encoding, we know that the number is negative because the leftmost binary digit is a 1. To convert this into a decimal number we would have to reverse the 2's complement (subtract 1 then do the one's complement) resulting in: 00000000 00000101 10011110 00100000 which equals -368160 as suspected.

    An example of a DATE subtraction resulting in a decimal difference:

    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
    

    The difference between those 2 dates is 0.25 days or 6 hours.

    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
    

    Now this time, since the difference is 0 days and 6 hours, it is expected that the first 4 bytes are 0. For the last 4 bytes, we can reverse them (because CPU is little-endian) and get 84,96 = 01010100 01100000 base 2 = 21600 in decimal. Converting 21600 seconds to hours gives you 6 hours which is the difference which we expected.

    Hope this helps anyone who was wondering how a DATE subtraction is actually stored.

    0 讨论(0)
  • 2020-12-03 00:13

    A few points:

    • Subtracting one date from another results in a number; subtracting one timestamp from another results in an interval.

    • Oracle converts timestamps to dates internally when performing timestamp arithmetic.

    • Interval constants cannot be used in either date or timestamp arithmetic.

    Oracle 11gR2 SQL Reference Datetime Matrix

    0 讨论(0)
  • 2020-12-03 00:14

    Use extract() function to retrieve hour / minute / seconds from interval value. See below example, how to get hours from two timestamp columns. Hope this helps!

    select INS_TS, MAIL_SENT_TS, extract( hour from (INS_TS - MAIL_SENT_TS) ) hourDiff from MAIL_NTFCTN;

    0 讨论(0)
提交回复
热议问题