Ora-01821 date format not recognized - sql

I want to change the string into date field but I received the error ora- 01821 date format not recognized while executing the following query.also I need am/pm in the db .how to achieve that
update bucket set closed = to_date(closed_on,'dd-mon-yy hh.mi.ss.ff a.m')
Closed_on value will be like 29-MAY-19 09.01.16.00000000 PM

I think you want to use TO_TIMESTAMP - date cannot store your milliseconds, so it doesn't understand the FF
select to_timestamp('29-MAY-19 09.01.16.00000000 PM','dd-mon-yy hh.mi.ss.FF8 AM') from dual
Note, as GMB has pointed out, you can't get oracle to parse a.m - you'll have to make it either a.m. or am. No half measures :) (hopefully its a copy pasta error)
If you're desperate to use TO_DATE, cut the milliseconds off:
select
to_date(
SUBSTR('29-MAY-19 09.01.16.00000000 PM', 1, 18) || SUBSTR('29-MAY-19 09.01.16.00000000 PM', -2, 2),
'dd-mon-yy hh.mi.ssAM'
)
from dual
If your time str is a.m. make the second SUBSTR use , -4, 4) - "start from right, 4 places left, then take 4 chars"
If the millis are always 00000000 you could neaten this up with a REPLACE(timeStr, '.00000000 ', '') instead

Fractional seconds (FF) do not with data type DATE. So if your table's column closed_on is of type Date, it will not work.
Caius Jard have suggested good alternative solutions.

Another option is to skip the characters using # as follows:
SQL> SELECT
2 TO_DATE('29-MAY-19 09.01.16.00000000 PM', 'DD-MON-YYYY HH.MI.SS.######## AM')
3 FROM
4 DUAL;
TO_DATE('29-MAY-1909.01
-----------------------
29-MAY-0019 09.01.16 PM
SQL>
The number of # should be number of characters to be skipped.

Related

how to add the current date to time column in oracle sql

I have a table which hold only time part.
TIME
08:00
08:00
08:00
08:00
18:00
19:00
20:00
Now, i need to concatenate the time with current date. for that i tried with below code.
to_date( to_char (sysdate,'YYYYMMDD')|| Time,'HH24MI')
but that is not working as expected and getting the below error
ORA-01830: date format picture ends before converting entire input string
Can you please help me to solve this problem.
Expected output(date time format):
14-Feb-23 08:00
14-Feb-23 09:00
What datatype do you expect as result? A string?
SQL> with test (time) as
2 (select '08:00' from dual)
3 select to_char(sysdate, 'dd-Mon-yy ') || time as result
4 from test;
RESULT
---------------
14-Feb-23 08:00
If you expect date, then
SQL> with test (time) as
2 (select '08:00' from dual)
3 select to_date(to_char(sysdate, 'dd-Mon-yy ') || time, 'dd-Mon-yy hh24:mi') as result
4 from test;
RESULT
--------
14.02.23
Hm, what format is that?! My default! Modify it to something else, e.g. what you said you want but - nonetheless - value previous code returns is a valid date value:
SQL> alter session set nls_date_format = 'dd-Mon-yy hh24:mi';
Session altered.
SQL> with test (time) as
2 (select '08:00' from dual)
3 select to_date(to_char(sysdate, 'dd-Mon-yy ') || time, 'dd-Mon-yy hh24:mi') as result
4 from test;
RESULT
---------------
14-Feb-23 08:00
SQL>
You have time strings like '08:00'. And you have to_date( to_char (sysdate,'YYYYMMDD')|| Time,'HH24MI').
The expression to_char (sysdate,'YYYYMMDD')|| Time results in something like '2023021408:00'.
You want to convert this result string into a datetime (that Oracle inappropriately calls DATE) with TO_DATE: to_date('2023021408:00', 'HH24MI').
You do notice, that the string you have constructed and the format you are using don't match? Your string's format is not 'HH24MI', but 'YYYYMMDDHH24:MI'.
Hence use:
TO_DATE(TO_CHAR(SYSDATE,'YYYYMMDD') || time, 'YYYYMMDDHH24:MI')

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)

Convert SQL string like "1:00 PM - 9:00 PM" into numerical hour columns

For Redshift
I have strings like "1:00 PM - 9:15 PM" in a column called workhours.
What's the most efficient way in SQL to convert this type of string in this column to two columns (start time, end time), resembling something like :
start_time | end_time
13:00 21:15
I know the first step is start with:
select
split_part(workhours, '-', 1) as start_time,
split_part(workhours, '-', 2) as end_time
but from here, what's the best way to turn the "1:00PM" into "13:00" and the "9:15PM" into "21:15"?
Edit: I would also like the solution to be able to automatically accommodate both AM and PM without me having to manually specific which (hope that makes sense).
Thanks in advance!
The following solution builds a dummy timestamp using January 1st of the current year, along with the TO_TIMESTAMP function, which the latest Redshift should support. It then extracts out the 24 hour time portion using TO_CHAR.
SELECT
TO_CHAR(TO_TIMESTAMP('2018-01-01 ' || SPLIT_PART('1:00 PM - 9:15 PM', '-', 1),
'YYYY-MM-DD HH:MI PM'), 'HH24:MI:SS') AS part1,
TO_CHAR(TO_TIMESTAMP('2018-01-01 ' || SPLIT_PART('1:00 PM - 9:15 PM', '-', 2),
'YYYY-MM-DD HH:MI PM'), 'HH24:MI:SS') AS part2;
Demo
But I think the best long term solution is to just store date information as a date type in your database. Most likely, there is some notion of date associated with these times, and storing time by itself is probably not a use case which will come up often in real life.

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?