inserting systimestamp that is precision 9 - sql

I'm trying to get a oracle timestamp(9) populated with the full 9 precision at instert with systimestamp
column MY_TS format 99999999999999999999999999
SElECT TO_NUMBER(TO_CHAR(systimestamp, 'YYYYMMDDHH24MISSSSSFF'), '99999999999999999999999999') as MY_TS FROM DUAL;
CREATE TABLE T1 (MY_TS timestamp(9));
INSERT INTO T1 VALUES(systimestamp);
SELECT TO_NUMBER(TO_CHAR(MY_TS, 'YYYYMMDDHH24MISSSSSFF'), '99999999999999999999999999') as MY_TS FROM T1;
Yeilds
MY_TS
---------------------------
20180802152155270705139
Table created.
1 row created.
MY_TS
---------------------------
20180802152155270735103000
Which seems to be loosing the last 3 digit precision.
I keep reading that the Oracle default for systimestamp is 6, but can be modified. Yet I can't seem to run into a page that says how that is done (most are concerned with formation from the value).

Which seems to be loosing the last 3 digit precision.
It is not losing the last 3 digits precision; the FF format model is showing the default precision of the data type:
FF [1..9]
Fractional seconds; no radix character is printed (use the X format element to add the radix character). Use the numbers 1 to 9 after FF to specify the number of digits in the fractional second portion of the datetime value returned. If you do not specify a digit, then Oracle Database uses the precision specified for the datetime datatype or the datatype's default precision.
SELECT TO_CHAR(systimestamp, 'YYYYMMDDHH24MISSFF') as MY_TS FROM DUAL
UNION ALL
SELECT 'YYYYMMDDHHMMSSFFFFFFFFF' FROM DUAL;
Output:
MY_TS
-----------------------
20180802230042489334
YYYYMMDDHHMMSSFFFFFFFFF
So, it is only outputting fractional seconds to microsecond precision which is the default precision:
TIMESTAMP [(fractional_seconds_precision)]
Year, month, and day values of date, as well as hour, minute, and second values of time, where fractional_seconds_precision is the number of digits in the fractional part of the SECOND datetime field. Accepted values of fractional_seconds_precision are 0 to 9. The default is 6. The default format is determined explicitly by the NLS_DATE_FORMAT parameter or implicitly by the NLS_TERRITORY parameter. The sizes varies from 7 to 11 bytes, depending on the precision. This datatype contains the datetime fields YEAR, MONTH, DAY, HOUR, MINUTE, and SECOND. It contains fractional seconds but does not have a time zone.
If you change the precision of the timestamp:
SELECT TO_CHAR(systimestamp(9), 'YYYYMMDDHH24MISSFF') as MY_TS FROM DUAL
UNION ALL
SELECT 'YYYYMMDDHHMMSSFFFFFFFFF' FROM DUAL;
Output:
MY_TS
-----------------------
20180802230708135745000
YYYYMMDDHHMMSSFFFFFFFFF
Which gives a TIMESTAMP with 9 digits precision.
You could also specify the precision in the output format using the FF9 format model (rather than just FF which uses the data type's default precision):
SELECT TO_CHAR(systimestamp, 'YYYYMMDDHH24MISSFF9') as MY_TS FROM DUAL
UNION ALL
SELECT 'YYYYMMDDHHMMSSFFFFFFFFF' FROM DUAL;
Output:
MY_TS
-----------------------
20180802230936652602000
YYYYMMDDHHMMSSFFFFFFFFF

Related

Create a datetime value from separate date time columns

I have individual columns for year, month, day, hour, minute, second and millisecond. I need to combine them all together into a date timestamp. I am able to make a date value, but can't seem to make a valid time one. I am working in Oracle and I have sample below. I'd like my value to look like the createtime column.
This did the trick:
to_timestamp(XT.MONTH1||'-'||XT.DAY1||'-'||XT.YEAR||' '|| xt.hour24||'.'||xt.minute||'.'||xt.second||'.'||xt.milliseconds,'MM-DD-YYYY HH24:MI:SS.FF')
That is quite easy but not pretty. You convert the columns from datatype NUMBER to VARCHAR2 padded with leading zeros, using fm to suppress any spaces for + or -:
SELECT TO_CHAR(month,'fm00') FROM mytable;
05
Next, you glue the converted columns together with the || operator to get a single, long, string:
SELECT TO_CHAR(year,'fm0000')||TO_CHAR(month,'fm00')|| ...
20200519...
This long string can now be converted to datatype DATE, or, in your case TIMESTAMP as you have milliseconds. You need to specify the date format you used, f.i. 'YYYYMMDD...'
SELECT TO_TIMESTAMP(TO_CHAR(year,'fm0000')|| ... , 'YYYYMM')
A complete example looks like:
SELECT TO_TIMESTAMP(
TO_CHAR(year,'fm0000')||TO_CHAR(month,'fm00')||
TO_CHAR(day,'fm00')||TO_CHAR(hour24,'fm00')||
TO_CHAR(minute,'fm00')||TO_CHAR(second,'fm00')||
TO_CHAR(ms,'fm000')
, 'YYYYMMDDHH24MISSFF3')
FROM (-- your table, as a mockup, I'll use DUAL
SELECT 2020 as year, 5 as month, 19 as day,
13 as hour24, 7 as minute, 10 as second,
300 as ms
FROM DUAL);
2020-05-19 13:07:10,300000000
EDIT:
The fill mode modifier fm supresses a leading space for positive numbers (to make room for the - sign for negative numbers). All parts of a date are positive, so you get a lot of spaces in your string.
SELECT TO_CHAR(x,'99'), TO_CHAR(x,'fm99')
FROM (SELECT -10 as x FROM DUAL UNION ALL SELECT 10 FROM DUAL);
| 10|10|
|-10|-10|
The documentation is a bit hidden under Format Model Modifiers.
Come to think of it, you might as well keep the spaces and adjust your format model:
SELECT TO_TIMESTAMP(
TO_CHAR(year,'0000')||TO_CHAR(month,'00')||
TO_CHAR(day,'00')||TO_CHAR(hour24,'00')||
TO_CHAR(minute,'00')||TO_CHAR(second,'00')||
TO_CHAR(ms,'000')
,' YYYY MM DD HH24 MI SS FF3')
FROm (SELECT 2020 as year, 5 as month, 19 as day,
13 as hour24, 7 as minute, 10 as second,
300 as ms FROM DUAL);

Substraction with decimal in ORACLE SQL

I need to substract 2 timestamps in the given format:
16/01/17 07:01:06,165000000
16/01/17 07:01:06,244000000
I want to express the result with 2 decimal values but somewhere in the CAST process I am loosing precision. My atempt by now goes this way:
select
id,
trunc((CAST(MAX(T.TIMESTAMP) AS DATE) - CAST(MIN(T.TIMESTAMP) AS DATE))*24*60*60,2) as result
from table T
group by id;
But I get id_1 '0' as a result for the two timestamps above even after I set the truncate decimals at 2.
Is there a way that I can obtain the 0.XX aa a result of the substraction?
It's because you are casting the timestamp to date.
Use to_timestamp to convert your string into timestamp.
Try this:
with your_table(tstamp) as (
select '16/01/17 07:01:06,165000000' from dual union all
select '16/01/17 07:01:06,244000000' from dual
),
your_table_casted as (
select to_timestamp(tstamp,'dd/mm/yy hh24:mi:ss,ff') tstamp from your_table
)
select trunc(sysdate + (max(tstamp) - min(tstamp)) * 86400 - sysdate, 2) diff
from your_table_casted;
The difference between two timestamps is INTERVAL DAY TO SECOND.
To convert it into seconds, use the above trick.
DATE—This datatype stores a date and a time, resolved to the second. It does not include the time zone. DATE is the oldest and most commonly used datatype for working with dates in Oracle applications.
TIMESTAMP—Time stamps are similar to dates, but with these two key distinctions: you can store and manipulate times resolved to the nearest billionth of a second (9 decimal places of precision), and you can associate a time zone with a time stamp, and Oracle Database will take that time zone into account when manipulating the time stamp.
The result of a substraction of two timestamps is an INTERVAL:
INTERVAL—Whereas DATE and TIMESTAMP record a specific point in time, INTERVAL records and computes a time duration. You can specify an interval in terms of years and months, or days and seconds.
You can find more information here

Varchar2 to Date for sysdate comparison

A table I am querying has dates stored as varchar2. I need to compare the dates stored as varchar2 with sysdate using BETWEEN sysdate-1 AND sysdate-30 (to return varchar2 dates from the last month).
When specifying TO_DATE(varchar2, 'DD-MON-YYYY') I get error "literal does not match format string".
I am stuck as Oracle documentation says this is an acceptable format for TO_DATE() when converting from varchar2.
UPDATE: This is a corporate database, I did not design the DB and can only work with what I have available. The data set is enormous and is automatically updated by SCADA devices, over 10,000 devices daily.
SELECT device_name, read_date, sysdate
FROM oracle_database
------- Data Returned by query --------
device_name read_date sysdate
Device 1 5/14/2013 22-Sep-14
Device 2 5/14/2013 22-Sep-14
Device 3 5/14/2013 22-Sep-14
Device 4 5/14/2013 22-Sep-14
Device 5 5/14/2013 22-Sep-14
Device 6 5/14/2013 22-Sep-14
Device 7 5/14/2013 22-Sep-14
Results from using TO_DATE
SELECT device_name, TO_DATE(read_date, 'DD-MON-YY'), sysdate
------- Data Returned by query ----------
ORA-01861: literal does not match format string
Given your data, the proper format is very probably MM/DD/YYYY. It will match both single and double digits months or days.
MM : month from 01 to 12 (leading 0 not mandatory by default)
\ : any punctuation sign
DD : day from 01 to 31 (leading 0 not mandatory by default)
\ : any punctuation sign
YYYY : year
See Oracle's documentation about datetime Format Models for the details.
Here is example:
-- Some test data
WITH testdata AS (
SELECT '5/14/2013' as d FROM DUAL
UNION SELECT '05/14/2013' FROM DUAL
UNION SELECT '5/1/2013' FROM DUAL
UNION SELECT '5/01/2013' FROM DUAL
UNION SELECT '6-6-2013' FROM DUAL)
-- Actual query demonstrating the use of the MM/DD/YYYY format
select d, TO_DATE(d,'MM/DD/YYYY') FROM testdata
Producing:
D TO_DATE(D,'MM/DD/YYYY')
05/14/2013 05/14/2013
5/01/2013 05/01/2013
5/1/2013 05/01/2013
5/14/2013 05/14/2013
6-6-2013 06/06/2013
If you really need to enforce the fact that your date components are separated by / (and not by any other punctuation sign), you should use fxfmMM/DD/YYYY instead:
the fx flag force strict comparison, both for punctuation and number of digits in the format
the fm flag relax that comparison to allow up to the number of specified digits
SELECT device_name, TO_DATE(read_date, 'DD/MM/YYYY'), sysdate
Firstly, your design is flawed. You should never have DATE as VARCHAR2 data type.
Secondly, while comparing the dates, and converting a date literal to a DATE, always specify the same format mask on both sides.
To convert a string to date, use TO_DATE with proper format mask.
To convert a date to string, use TO_CHAR with same format mask.
Make sure, while comparing the values in an expression, you explicitly convert the data type on either sides of the comparison operator to avoid implicit data type conversion.
And you must read this excellent article bybEd Stevens, http://edstevensdba.wordpress.com/2011/04/07/nls_date_format/

SQL How to parse text value(VARCHAR) and then get milliseconds value?

There are varchar column may contain this examples (only 3 variants of values):
Oct 15, 2013 |
15/10/2013 |
2013-10-15
Need to update column values and set the appropriate values for milliseconds: 1381723200000. Without changing type column.
Oracle Perspective:
If you want to extract Milliseconds from a string with YYYY-DD-MM format or whatever (without milliseconds)
SELECT
TO_CHAR( TO_TIMESTAMP ( '2013-10-15',
'YYYY-MM-DD' ),'FF9')
FROM
DUAL;
will give you 000000000 always in Oracle, since there is no milliseconds value stored in your string.
But, if you want to convert the date into milliseconds, then
Milliseconds since when???
SELECT
( TO_DATE ( '2013-10-16',
'YYYY-MM-DD' ) -- starting date
- TO_DATE ( '2013-10-15',
'YYYY-MM-DD' )) -- ending date
*24*60*60*1000 -- milliseconds multiplication factor
FROM
DUAL;

Subtracting Dates in Oracle - Number or Interval Datatype?

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 datatypes, the result will be a NUMBER datatype.
On cursory testing, this appears to be true:
CREATE TABLE test (start_date DATE);
INSERT INTO test (start_date) VALUES (date'2004-08-08');
SELECT (SYSDATE - start_date) from test;
will return a NUMBER datatype.
But now if you do:
SELECT (SYSDATE - start_date) DAY(5) TO SECOND from test;
you get an INTERVAL datatype. In other words, Oracle can convert the NUMBER from the DATE subtraction into an INTERVAL type.
So now I figured I could try putting in a NUMBER datatype directly in the brackets (instead of doing 'SYSDATE - start_date' which results in a NUMBER anyways):
SELECT (1242.12423) DAY(5) TO SECOND from test;
But this results in the error:
ORA-30083: syntax error was found in interval value expression
So my question is: what's going on here? It seems like subtracting dates should lead to a NUMBER (as demonstrated in SELECT statement #1), which CANNOT be automatically cast to INTERVAL type (as demonstrated in SELECT statement #3). But Oracle seems to be able to do that somehow if you use the DATE subtraction expression instead of putting in a raw NUMBER (SELECT statement #2).
Thanks
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.
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.
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
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;
select TIMEDIFF (STR_TO_DATE('07:15 PM', '%h:%i %p') , STR_TO_DATE('9:58 AM', '%h:%i %p'))