convert timestamp format to integer in oracle - sql

I have timestamp value in the format "01-JAN-00 10.05.54.170489000 AM". When I tried to convert to integer using the format "yyyymmddhhmm24missff":
select to_number(to_char(date_column,'yyyymmddhhmm24missff') from table_name;
I am getting the value as 20000101100554170489.The last 3 digits are missing. I need exact value as 20000101100554170489000.
How can I keep those last three digits in my number?

Your query (with minor correction) gets the result you wanted when I run it with a plain timestamp, supplied via a CTE:
set numwidth 23
alter session set nls_timestamp_format = 'DD-MON-RR HH:MI:SS.FF AM';
with table_name (date_column) as (
select to_timestamp('01-JAN-00 10.05.54.170489000 AM', 'DD-MON-RR HH:MI:SS.FF AM')
from dual
)
select date_column, to_number(to_char(date_column,'yyyymmddhh24missff')) as result
from table_name;
DATE_COLUMN RESULT
------------------------------- -----------------------
01-JAN-00 10:05:54.170489000 AM 20000101100554170489000
The only way I can see to get the result you actually have is if your timestamp column is actually constrained to that precision; i.e. date_column timestamp(6):
with table_name (date_column) as (
select cast(to_timestamp('01-JAN-00 10.05.54.170489000 AM', 'DD-MON-RR HH:MI:SS.FF AM')
as timestamp(6))
from dual
)
select date_column, to_number(to_char(date_column,'yyyymmddhh24missff')) as result
from table_name;
DATE_COLUMN RESULT
------------------------------- -----------------------
01-JAN-00 10:05:54.170489000 AM 20000101100554170489
It seems odd to want to show more precision than the value is actually allowed to have, but if you really want to always include those last three zeros then you can override the column precision by using ff9:
with table_name (date_column) as (
select cast(to_timestamp('01-JAN-00 10.05.54.170489000 AM', 'DD-MON-RR HH:MI:SS.FF AM')
as timestamp(6))
from dual
)
select date_column, to_number(to_char(date_column,'yyyymmddhh24missff9')) as result
from table_name;
DATE_COLUMN RESULT
------------------------------- -----------------------
01-JAN-00 10:05:54.170489000 AM 20000101100554170489000
A timestamp (or date) doesn't have an intrinsic format, you are just seeing it converted to a string by your client, probably based on your session NLS settings. I've run an alter session to match what you seem to be using. Notice that even when I constrained the data type to timestamp(6), the client's default formatting using ff still showed nine digits of precision. Seeing all nine when you query the table doesn't mean the values can actually have that much precision, and the last three digits displayed will always be zeros in this case.

Related

How String to Timestamp works without explicit conversion in oracle where clause

Both of following SQL works in my Oracle DB, the result or the count of results are completely same and correct. CREATE_TIME is a timestamp column.
select * from EMPLOYEE where CREATE_TIME =(or >) '2022-01-21 2:49:38.251'
select * from EMPLOYEE where CREATE_TIME =(or >) '22-01-21 2:49:38.251000'
Quite curious on how it works because the String format is different, I didn't use TO_* conversion function and the Oracle NLS(as default conversion rules) shows completely different format.
PARAMETER -> VALUE
NLS_TIMESTAMP_TZ_FORMAT -> DD-MON-RR HH.MI.SSXFF AM TZR
NLS_TIME_TZ_FORMAT -> HH.MI.SSXFF AM TZR
NLS_TIMESTAMP_FORMAT -> DD-MON-RR HH.MI.SSXFF AM
NLS_TIME_FORMAT -> HH.MI.SSXFF AM
Searched the information but didn't find the answer, it would be appreciate if anyone could answer me and provide a information/document link for reference.
If you try to perform an explicit cast using CAST(value AS TIMESTAMP), or an equivalent implicit cast such as in your query, from the string values to a timestamp then Oracle will implicitly convert the cast to the equivalent of:
TO_TIMESTAMP(
value,
(SELECT value FROM NLS_SESSION_PARAMETERS WHERE parameter = 'NLS_TIMESTAMP_FORMAT')
)
If you have the sample data:
CREATE TABLE table_name (value) AS
SELECT '2022-01-21 2:49:38.251' FROM DUAL UNION ALL
SELECT '22-01-21 2:49:38.251000' FROM DUAL;
You can see it in action using:
ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'DD-MON-RR HH24.MI.SSXFF';
SELECT value,
TO_CHAR(
CAST(value AS TIMESTAMP),
'YYYY-MM-DD HH24:MI:SS.FF'
) AS converted_value
FROM table_name;
Outputs the error:
ORA-01843: not a valid month
As, given the string-to-date conversion rules the MON format model will also match MONTH but it will not match the numeric MM format so the 01 month generates the error.
However:
ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'RR-MM-DD HH24:MI:SSXFF'
SELECT value,
TO_CHAR(
CAST(value AS TIMESTAMP),
'YYYY-MM-DD HH24:MI:SS.FF'
) AS converted_value
FROM table_name;
Outputs:
VALUE
CONVERTED_VALUE
2022-01-21 2:49:38.251
2022-01-21 02:49:38.251000
22-01-21 2:49:38.251000
2022-01-21 02:49:38.251000
Both the rows are converted as expected.
If, instead, you use:
ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SSXFF'
SELECT value,
TO_CHAR(
CAST(value AS TIMESTAMP),
'YYYY-MM-DD HH24:MI:SS.FF'
) AS converted_value
FROM table_name;
Then the output is:
VALUE
CONVERTED_VALUE
2022-01-21 2:49:38.251
2022-01-21 02:49:38.251000
22-01-21 2:49:38.251000
0022-01-21 02:49:38.251000
And the first row converts as expected but the second row the YYYY format model matches 22 and gives 22 AD rather than 2022 AD (as was probably expected).
If you want to compare to a timestamp then either use an explicit conversion:
SELECT *
FROM EMPLOYEE
WHERE CREATE_TIME = TO_TIMESTAMP(
'2022-01-21 2:49:38.251',
'YYYY-MM-DD HH24:MI:SSXFF'
)
Or a timestamp literal:
SELECT *
FROM EMPLOYEE
WHERE CREATE_TIME = TIMESTAMP '2022-01-21 02:49:38.251'
If you rely on the NLS parameters then your query may have different (and unexpected) behaviours for different sessions (sometimes even for different sessions of the same user).
db<>fiddle here

ORACLE using TO_DATE to check if item is within last hour

I have a query that I am trying to use TO_DATE to check if ERROR_DT is a data that is within one hour of the current time
Here is what I have so far
SELECT BERROR_DT FROM SomeTable
WHERE ERROR_DT>=TO_CHAR(TO_DATE( SYSDATE, 'MM/DD/YYYY HH12:MI:SS AM') -1, 'fmMM/DDfm/YYYY HH12:MI:SS AM');
Error_DT has a value of (e.g.) 5/18/2020 6:45:15 PM
When I run this I get
ORA-01830: date format picture ends before converting entire input string
I followed the said link and it still is not working. How would I fix this so that I can still remove all 0s in front of the month and the date?
I would suggest converting the date string to the corresponding date value, then do the comparison:
select berror_dt
from sometable
where to_date(error_dt, 'fmMM/DD/YYYY HH12:MI:SS AM') >= sysdate - interval '1' hour
Bottom line, you should fix your data model and store dates as a date-like datatype rather than as a string. The above predicate is not efficient, because the conversion needs to be executed for each and every value of error_dt before the filtering applies, hence defeating an existing index on the column.
Obviously wrong thing you're doing is applying TO_DATE to SYSDATE which is a function that returns DATE datatype.
What you could do is to subtract sysdate and error_dt (I presume its datatype is DATE as well) and see whether difference is less than 1 hour. As difference of two dates is number of days, you have to divide it by 24 (as there are 24 hours in a day).
Something like this:
SQL> alter session set nls_date_format = ' dd.mm.yyyy hh:mi:ss am';
Session altered.
SQL> with test (id, error_dt) as
2 (select 1, to_date('18.05.2020 10:30:15 PM', 'dd.mm.yyyy hh:mi:ss am') from dual
3 union all
4 select 2, to_date('18.05.2020 05:20:55 AM', 'dd.mm.yyyy hh:mi:ss am') from dual)
5 select t.id, t.error_dt, sysdate
6 from test t
7 where sysdate - t.error_dt < 1 / 24;
ID ERROR_DT SYSDATE
---------- ----------------------- -----------------------
1 18.05.2020 10:30:15 PM 18.05.2020 11:02:24 PM
SQL>
If ERROR_DT is a DATE value you just need to use something like
SELECT BERROR_DT
FROM SomeTable
WHERE ERROR_DT >= SYSDATE - INTERVAL '1' HOUR
or if you prefer to use old-fashioned pre-INTERVAL calculations
SELECT BERROR_DT
FROM SomeTable
WHERE ERROR_DT >= SYSDATE - (1/24)

Why doesn't date subtraction produce whole numbers when casting TIMESTAMP to DATE?

Normal date subtraction looks like this:
SELECT TO_DATE('12-29-2019') - TO_DATE('12-20-2019') FROM DUAL
/* RESULT: 9 */
When I cast a TIMESTAMP to a DATE, Oracle truncates the hours/minutes/seconds and produces a "whole" DATE value.
SELECT CAST(LOCALTIMESTAMP AS DATE) FROM DUAL
/* RESULT: 12/07/2019 */
But when performing date subtraction with a CAST from a TIMESTAMP, I don't get whole numbers anymore.
SELECT TO_DATE('12-29-2019') - CAST(LOCALTIMESTAMP AS DATE) FROM DUAL
/* RESULT: 21.0999421296296296296296296296296296296 */
Why doesn't date subtraction produce whole numbers when casting TIMESTAMP to DATE in Oracle?
Because what you see is not what you have.
This was your command and result:
SQL> SELECT CAST(LOCALTIMESTAMP AS DATE) FROM DUAL;
CAST(LOC
--------
07.12.19
But, that's just because date format was set as such.
If you alter session and set different format, then you get
SQL> alter session set nls_date_format = 'dd.mm.yyyy hh24:mi:ss';
Session altered.
SQL> SELECT CAST(LOCALTIMESTAMP AS DATE) FROM DUAL;
CAST(LOCALTIMESTAMP
-------------------
07.12.2019 22:44:47
SQL>
which is quite different, is it not? And that's why you got decimal number as a result. TRUNC it first to remove time component.

Using TO_DATE() with AM/PM Formatting

I am trying to select some dates from a table where the format of the dates is like this:
14-APR-14 10.35.00.0000000000 AM
01-NOV-16 02.43.00.0000000000 PM
Note that the dates can be either AM or PM, but when I try to do a simple SELECT from the table such as:
SELECT * FROM MyTable
WHERE TO_DATE(MyDate, 'DD-MON-YYYY HH:MI:SS AM') > '31-DEC-2016 08:00:00 AM';
I get the error:
ORA-01855: AM/A.M. or PM/P.M. required
I've been trying to get this work for some time but with no luck. Any help here would be appreciated.
Several problems.
Your inputs are obviously strings, since they have ten decimal places and timestamps in Oracle have at most 9. Then, strings with fractions of a second can't be converted to a date with to_date - you need to use to_timestamp or else you need to remove all the fractional parts. In the solution below I only remove the last (the tenth) decimal, since you may have non-zero fractional parts in the table - although not in the sample you posted.
Then, your format mask has yyyy but your inputs have only two digits for the year (which probably means 93 means 1993 and not 2093, so the correct thing to use would be rr rather than yy). And you use : in the format mask where your inputs use .
Finally, don't even compare dates in string format: in string comparisons, 01-JAN-2015 is before 20-NOV-2013.
You probably want something like this:
select mydate
from (
select '14-APR-14 10.35.00.0000000000 AM' as mydate from dual
union all
select '01-NOV-16 02.43.00.0000000000 PM' from dual
) mytable
where to_timestamp(substr(mydate, 1, 28) || substr(mydate, -3), 'dd-MON-rr hh.mi.ss.ff AM')
> to_timestamp('31-DEC-2016 08:00:00 AM', 'dd-MON-yyyy hh:mi:ss AM');
This query compiles correctly, and it produces no rows in the output (for obvious reasons).
NOTE: In a comment you (the OP) say the mydate field is a timestamp(6) datatype. Hard to believe (you show ten decimal places), but if indeed it is a timestamp or date, then you don't need to wrap it within any to_timestamp or to_date function, it should stand alone in the left-hand side of the inequality.
From your comment:
It's actually a timestamp; not a string. Timestamp(6) to be precise
You can just use a TIMESTAMP literal:
SELECT *
FROM MyTable
WHERE MyDate > TIMESTAMP '2016-12-31 08:00:00';

Why does RPAD() on a Date column return only the date component?

I have a column in a table with a data type as DATE. When I fetch the column via a query (I've used SYSDATE as an example, but the behavior is the same), I get the date/time - which I understand.
SELECT SYSDATE
FROM DUAL
SYSDATE
--------------------
21-Feb-11 12:24:39 PM
Now, using rpad() returns only the date part
SELECT SYSDATE, RPAD(SYSDATE, '9')
FROM DUAL
SYSDATE | RPAD(SYSDATE, '9')
----------------------|-------------------
21-Feb-11 12:27:14 PM | 21-FEB-11
Oracle documentation states:
RPAD returns expr1, right-padded to length n characters with expr2, replicated as many times as necessary. If expr1 is longer than n, then this function returns the portion of expr1 that fits in n.
Now sysdate returns characters > 9, so why doesn't, say rpad(16) return the date and the time ?
SELECT SYSDATE, RPAD(SYSDATE, '16')
FROM DUAL
SYSDATE | RPAD(SYSDATE, '16')
----------------------|-------------------
21-Feb-11 12:27:14 PM | 21-FEB-11
RPAD is a string function, so when you apply it to a DATE value Oracle first has to implicitly convert the date to a string, which it does using the session's default format mask, which usually does not include the time component. Try this instead:
SELECT SYSDATE, RPAD (TO_CHAR(SYSDATE,'DD-Mon-YY HH:MI:SS'), 16)
FROM DUAL;
Having said that, you are getting the time when you just select SYSDATE. If I try to replicate your case I see this:
SQL> alter session set nls_date_format = 'DD-MON-RR HH24:MI:SS';
SQL> select sysdate, rpad(sysdate,16) from dual;
SYSDATE RPAD(SYSDATE,16)
------------------ ----------------
21-FEB-11 11:20:20 21-FEB-11 11:20:
i.e. pretty much what you were hoping to see. Which makes me wonder: how are you setting the format so that SELECT SYSDATE FROM DUAL shows the time?