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

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;

Related

SQL: Combine columns to specific datetime and cast to UTC

I am using Oracle SQL. I want to convert three columns to datetime in sql.
My data looks like:
DAY (DATE) HOUR (NUMBER) HALFHOUR (NUMBER)
21.04.22 11 22
21.04.22 11 23
21.04.22 12 24
21.04.22 12 25
21.04.22 13 26
21.04.22 13 27
....
I need to combine each row to the following specific format, in one column:
2022-04-21T13:30:00.00Z
Moreover, it should be converted from an utc time where data comes from (like UTC+3) to UTC+0 automatically.
How do I do this? I googled a lot, but cant do it.
Thanks :)
The solution to your problem is:
SELECT T1.*,
TO_CHAR(FROM_TZ(TO_TIMESTAMP(DAY||' '||HOUR||':'||HALFHOUR, 'DD.MM.RR HH24:MI'), 'Europe/Berlin') AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.ff2"Z"') as time_utc
FROM T1;
TO_TIMESTAMP:
TO_TIMESTAMP converts char of CHAR, VARCHAR2, NCHAR, or NVARCHAR2
datatype to a value of TIMESTAMP datatype.
Syntax is:
TO_TIMESTAMP( string1 [, format_mask] ['nlsparam'] )
FROM_TZ:
In Oracle Database, the FROM_TZ() function converts a timestamp value
and a time zone to a TIMESTAMP WITH TIME ZONE value. Pass the
timestamp value and the time zone as two separate arguments, and the
function returns them as a TIMESTAMP WITH TIME ZONE value.
Syntax is :
FROM_TZ(timestamp_value, time_zone_value)
time_zone_value all possible values an be found out using the below query:
SELECT * FROM V$TIMEZONE_NAMES;
The output generated using above two functions is the time in the germany time-zone and is converted to UTC time zone using the " AT TIME ZONE 'UTC' "
Then using TO_CHAR it is converted to the required format.
You can see the working sample example at the below link:
https://dbfiddle.uk/?rdbms=oracle_11.2&fiddle=b030170a2c5536b4f80f1287bbfe4aca

Inserting Date gives error ORA-01861: literal does not match format string

Insert date into WSH_Delivery_Details_Interface (https://docs.oracle.com/cloud/r13_update17c/scmcs_gs/OEDSC/WSH_DELIVERY_DETAILS_tbl.htm) throws this error
Query :
insert into WSH_DEL_DETAILS_Interface
(DELIVERY_DETAIL_INTERFACE_ID, CREATION_DATE, Date_Requested)
values
(30010985553,
TO_DATE('11/12/2018T05:10:30-00:00', 'DD/MM/YYYY '),
TO_DATE('11/12/2018', 'DD/MM/YYYY'));
Sample Record in creation_date and date_requested column:
Date_Requested Creation_Date
16-JUN-10 17-JUN-10 03.40.31.865000000 PM
The error can be reduced to:
select TO_DATE('11/12/2018T05:10:30-00:00','DD/MM/YYYY ') from dual;
Error report -
ORA-01861: literal does not match format string
Which is reasonable as it clearly doesn't match. You need to include the time elements in your format mask, and also a character literal for the fixed 'T', and for the fixed time zone offset:
select TO_DATE('11/12/2018T05:10:30-00:00','DD/MM/YYYY"T"HH24:MI:SS"-00:00"') from dual;
TO_DATE('11/12/2018
-------------------
2018-12-11 05:10:30
If that 'time zone' part isn't fixed and needs to be honoured, then you can use to_timestamp_tz() instead of to_date():
select TO_TIMESTAMP_TZ('11/12/2018T05:10:30-00:00','DD/MM/YYYY"T"HH24:MI:SS.FFTZH:TZM')
from dual;
TO_TIMESTAMP_TZ('11/12/20
-------------------------
2018-12-11 05:10:30.0 GMT
I've included .FF in the format model as well as the time zone offset elements (you could use TZR instead of TZH:TZM if you might be passed regions instead of offsets), since your example of existing data has fractional seconds, even though your literal string does not in this case
And you can cast() that to a date or plain timestamp if necessary, or possibly normalise to UTC if the input values can be other zones/offsets:
select TO_TIMESTAMP_TZ('11/12/2018T04:10:30-01:00','DD/MM/YYYY"T"HH24:MI:SS.FFTZH:TZM') as orig,
SYS_EXTRACT_UTC(
TO_TIMESTAMP_TZ('11/12/2018T04:10:30-01:00','DD/MM/YYYY"T"HH24:MI:SS.FFTZH:TZM')) as utc
from dual;
ORIG UTC
---------------------------- ---------------------
2018-12-11 04:10:30.0 -01:00 2018-12-11 05:10:30.0

inserting systimestamp that is precision 9

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

What is Oracle's Default Date Format?

I have an Oracle DB, and I don't control the date format. I want to know what the date format is to ensure that searches like
select * from search where search_date>='03/16/2016 00:00:00'
work as expected.
Don't do that - you are relying on implicit data type conversion which is going to fail at some point.
You have two options:
1) Use a proper ANSI SQL date literal:
select *
from search
where search_date >= timestamp '2016-03-16 00:00:00';
2) use to_date() (or to_timestamp()) and use a custom format.
select *
from search
where search_date >= to_date('03/16/2016 00:00:00', 'mm/dd/yyyy hh24:mi:ss');
With to_date() you should avoid any format that is language dependent. Use numbers for the month, not abbreviations (e.g. 'Mar' or 'Apr') because they again rely on the client language.
More details can be found in the manual: https://docs.oracle.com/cd/E11882_01/server.112/e41084/sql_elements003.htm#SQLRF51062
Never rely on implicit data type conversion.
You can get all the NLS session parameters with the query:
SELECT * FROM NLS_SESSION_PARAMETERS;
or, if you have the permissions GRANT SELECT ON V_$PARAMETER TO YOUR_USERNAME;, you can use the command:
SHOW PARAMETER NLS;
If you just want the date format then you can do either:
SELECT * FROM NLS_SESSION_PARAMETERS WHERE PARAMETER = 'NLS_DATE_FORMAT';
or
SHOW PARAMETER NLS_DATE_FORMAT;
However, you could also use ANSI date (or timestamp) literals which are format agnostic. An ANSI date literal has the format DATE 'YYYY-MM-DD' and a timestamp literal has the format TIMESTAMP 'YYYY-MM-DD HH24:MI:SS.FF9'. So your query would be:
select * from search where search_date>= DATE '2016-03-16'
or
select * from search where search_date>= TIMESTAMP '2016-03-16 00:00:00'
What is Oracle's Default Date Format?
A DATE doesn't have any format. Oracle does not store dates in the format you see. It stores it internally in 7 bytes with each byte storing different components of the datetime value.
Byte Description
---- -------------------------------------------------
1 Century value but before storing it add 100 to it
2 Year and 100 is added to it before storing
3 Month
4 Day of the month
5 Hours but add 1 before storing it
6 Minutes but add 1 before storing it
7 Seconds but add 1 before storing it
To display, use TO_CHAR with proper FORMAT MODEL.
For comparing, use TO_DATE with proper FORMAT MODEL.
What you see as a format by default, is your locale specific NLS settings.
SQL> select parameter, value from v$nls_parameters where parameter='NLS_DATE_FORMAT';
PARAMETER VALUE
--------------- ----------------------------------------------------------------
NLS_DATE_FORMAT DD-MON-RR
SQL> select sysdate from dual;
SYSDATE
---------
17-MAR-16
SQL> select to_char(sysdate, 'mm/dd/yyyy hh24:mi:ss') from dual;
TO_CHAR(SYSDATE,'MM
-------------------
03/17/2016 12:48:41
SQL>
search_date>='03/16/2016 00:00:00'
You are comparing a DATE with a string literal. Always, explicitly convert the string into date using TO_DATE and proper format mask.
TO_DATE('03/16/2016', 'MM/DD/YYYY')
Or, if you dealing only with the date part and not concerned with the time portion, then use the ANSI date literal which uses a fixed format DATE 'YYYY-MM-DD'
DATE '2016-03-16'
You might just be lucky to get an output due to an implicit datatype conversion based on your locale specific NLS settings. Never ever rely on implicit datatype conversion, it might work for you, might fail for others where the nls settings are different.

Retrieve data within a date range in Oracle

I have a table tbldeptdivision as follows:
ID DEPTID DIVISIONID FROMDATE TODATE REMARKS
--- ------- ----------- ----------- ----------- --------
21 21 5 31-AUG-99 01-JAN-80 NULL
I have the query
select *
from tbldeptdivision
where deptid = 21
and trunc(sysdate) between to_date(fromdate,'dd-Mon-yyyy')
and to_date(todate,'dd-mon-yyyy');
It returns me no value. Can anybody say why? '31-AUG-99' is actually '31-AUG-1999' and '01-JAN-80' is actually '01-JAN-2080'. What will be the exact query?
Assume FROMDATE/TODATE datatype is varchar2 then when you do to_date;
select to_date('01-JAN-80','dd-mon-yyyy') from dual;
OutPut: January, 01 0080 00:00:00
So it wont be '01-JAN-2080' but '01-JAN-0080'
Even if FROMDATE/TODATE datatype is date tusing to_date is not a good idea;
create table t(c date);
insert into t select sysdate from dual;
select c, to_date(c,'dd-mon-yyyy') from t;
OutPut:
C TO_DATE(C,'DD-MON-YYYY')
August, 25 2015 10:55:36 August, 25 0015 00:00:00
Still the year is 0015 not 2015.
If your columns datatype is date then use trunc to get thedate portiondon't useto_date`.
select *
from tbldeptdivision
where deptid=21
and trunc(sysdate) between trunc(fromdate)
and trunc(todate)
As your todate is a date your problem stems from the useless conversion of the column's value from a date to a varchar and back to a date:
to_date() converts a VARCHAR to a DATE value. If the value you pass to that function is already a DATE Oracle will first implicitely convert your date to a varchar by applying the default NLS format and will then convert that varchar back to a date, again applying the default NLS format.
In the first (implicit) conversion you are losing the century in your year, which consequently is then wrong when the varchar is converted back to a date
So in your case the following is done due to the call to_date(fromdate,'dd-Mon-yyyy')
todate contains the (real) date value: 1980-01-30
the implicit conversion to a varchar makes that '01-JAN-80'
the conversion from the varchar to a date then assumes the year 80 should be 2080 (again based on the rules for implicit data type conversion).
The general rule is:
Do NOT use to_date() on a DATE (or TIMESTAMP) column
If you need to get rid of the time part in the DATE column use trunc() instead:
where trunc(sysdate) between trunc(fromdate) and trunc(todate)
Using functions on fields in your where clause slows down production. This is the same logic and will run faster.
where fromdate <= trunc(sysdate)
and todate > trunc(sysdate )