Oracle to_char in query is not working - oracle

Im trying to run this below query but its seems to be error
For those data types are varchar
Query
select *
from RP_REPORT_TEMP
where to_char(START_DATE,'FM DD YYYY HH24:MI:SS AM')
>= 'May 01 2016 00:00:00'
and to_char(END_DATE,'FM DD YYYY HH24:MI:SS PM')
<= 'May 31 2016 11:59:00'
and lower(rid) like '%a001%'
order by CAST(cid as INTEGER) asc
Table
|RID |START_DATE |END_DATE |
|--------------------|-------------------------- |-----------------------------|
|A001 |May 1 2016 12:00:00:000AM |May 31 2016 12:00:00:000PM |
|A001 |May 1 2016 12:00:00:000AM |May 31 2016 12:00:00:000PM |
|A001 |May 1 2016 12:00:00:000AM |May 31 2016 12:00:00:000PM |
While i tried to execute this query. Query return some error. How can i fix this ?
Error
SQL Error [1722] [42000]: ORA-01722: invalid number
java.sql.SQLSyntaxErrorException: ORA-01722: invalid number

Why don't you use TO_DATE() on the date instead?
select * from RP_REPORT_TEMP
where START_DATE >= to_date('May 01 2016 12:00:00 AM','MON DD YYYY HH:MI:SS AM')
and END_DATE <= to_date('May 31 2016 11:59:00 AM','MON DD YYYY HH24:MI:SS AM')
and lower(rid) like '%a001%'

Assuming that START_DATE and END_DATE are actually dates, I can spot two main issues in your code:
Casting every row to string is slow and also prevents Oracle from using indexes (unless you have a carefully built function-based index).
Lexicographical sort (aka "A to Z") does not render meaningful results with dates with "May 1". Just think of what sense this list has: Apr, Aug, Dec, Feb, Jan, Jul, Jun, Mar, May, Nov, Oct, Sep
My suggestions are:
Avoid converting dates to string, save for display purposes
Use a more solid format when you have to convert from string to date
As a result:
select *
from RP_REPORT_TEMP
where START_DATE
>= TO_DATE('2016-05-01 00:00:00', 'YYYY-MM-DD HH24:MI:SS')
and END_DATE
<= TO_DATE('2016-05-31 11:59:00', 'YYYY-MM-DD HH24:MI:SS')
and lower(rid) like '%a001%'
order by CAST(cid as INTEGER) asc
P.S. START_DATE and END_DATE were not dates after all

Try this..
select *
from RP_REPORT_TEMP
where TO_date(START_DATE,'MON DD YYYY HH:MI:SS AM')
>= to_Date('May 01 2016 12:00:00 AM' ,'MON DD YYYY HH:MI:SS AM')
and TO_DATE(END_DATE,'MON DD YYYY HH:MI:SS PM')
<= TO_DATE('May 31 2016 11:59:00 AM' ,'MON DD YYYY HH:MI:SS PM')
and lower(rid) like '%a001%'
order by CAST(cid as INTEGER) asc
select *
from RP_REPORT_TEMP
where TO_date(START_DATE,'MON DD YYYY HH24:MI:SS')
>= to_Date('May 01 2016 12:00:00' ,'MON DD YYYY HH24:MI:SS')
and TO_DATE(END_DATE,'MON DD YYYY HH24:MI:SS')
<= TO_DATE('May 31 2016 11:59:00' ,'MON DD YYYY HH24:MI:SS')
and lower(rid) like '%a001%'
order by CAST(cid as INTEGER) asc

This is what your query should look like:
select *
from rp_report_temp
where to_date(start_date,'FM Mon DD YYYY HH:MI:SS"FMMon DD YYYY HH:MI:SS":000"AM', 'nls_date_language = english')
>= to_date('01/05/2016 00:00:00', 'dd/mm/yyyy hh24:mi:ss')
and to_date(end_date,'FM Mon DD YYYY HH:MI:SS":000"AM', 'nls_date_language = english')
<= to_date('31/05/2016 23:59:59', 'dd/mm/yyyy hh24:mi:ss')
and lower(rid) like '%a001%'
order by cast(cid as integer) asc;
Here, I have converted your start and end date columns into a date, based on the fact that a sample string in that column looks like "May 1 2016 12:00:00:000AM" - there's an extra ":000" after the seconds; I'm assuming this is always ":000".
Note how I've made your code nls_date_language parameter independent, by specifically setting that value within the to_date(). This means that if someone else's client has that set to something that's not English, the sql statement will still work for them.
You'll note that I didn't need to do that for the values you're testing, since I changed the format of the string to something that used numbers to represent the months.
You have some fundamental data modelling issues here too - by storing everything as varchar2 you have made a rod for your own back - you've lost all the validation available (what happens if someone puts a string of Feb 30 2015 39:99:99 AM in that column? AWOOGA, AWOOGA, error!). You've made your queries have to work harder (now we've got functions all over the place trying to convert data to the correct datatype).
You should store data in the correct datatype. Dates should be stored as DATE, Timestamps as TIMESTAMP, numbers as NUMBER (with or without specific precision and scale), etc etc.
If your data was stored correctly, then your query would be:
select *
from rp_report_temp
where start_date >= to_date('01/05/2016 00:00:00', 'dd/mm/yyyy hh24:mi:ss')
and end_date <= to_date('31/05/2016 23:59:59', 'dd/mm/yyyy hh24:mi:ss')
and lower(rid) like '%a001%'
order by cid asc;

Related

VARCHAR2 to TIMESTAMP conversion in ORACLE 12c

I have a column in table A as
select create_time from table_a;
The value is
08-MAR-19 08.23.47.897000000 PM GMT.
This column has been marked as VARCHAR2 for some business purpose. Now I am trying to get this column and convert the value to TIMESTAMP for some purpose, like this as below:
SELECT TO_TIMESTAMP(create_time, 'DD-MON-YYYY HH.MI.SS.FF AM') from table_a;
But I am getting error:
ORA-01830: date format picture ends before converting entire input string
Can someone help me to convert this varchar data to timestamp. The reason I am trying to do is, I need to convert this time from one timezone to another :
eg:
SELECT FROM_TZ(TO_TIMESTAMP(create_time, 'DD-MON-YYYY HH.MI.SS.FF AM'), 'UTC') AT TIME ZONE 'CET' from table_a;
SQL> select replace('08-MAR-19 08.23.47.897000000 PM GMT','GMT','') AS RESULT from dual
;
RESULT
--------------------------------
08-MAR-19 08.23.47.897000000 PM
SQL> select to_timestamp(replace('08-MAR-19 08.23.47.897000000 PM GMT','GMT','')) as RESULT from dual ;
RESULT
---------------------------------------------------------------------------
08-MAR-19 08.23.47.897000000 PM
SQL> select from_tz(to_timestamp(replace('08-MAR-19 08.23.47.897000000 PM GMT','GMT','')),'UTC') AT TIME ZONE 'CET' AS RESULT from dual ;
RESULT
---------------------------------------------------------------------------
08-MAR-19 09.23.47.897000000 PM CET
SQL>
The four characters GMT at the end are not accounted for in your format string
SELECT TO_TIMESTAMP('08-MAR-19 08.23.47.897000000 PM GMT', 'DD-MON-YYYY HH.MI.SS.FF AM') from dual;
The following takes care of that:
SELECT TO_TIMESTAMP(substr(create_time, 1, LENGTH(create_time) -4), 'DD-MON-YYYY HH.MI.SS.FF AM') t from dual;
Try this.
select TO_TIMESTAMP( REPLACE(ts, 'GMT', '')) from test_timestamp;
Remove GMT using REPLACE
WITH A AS (SELECT REPLACE('08-MAR-19 08.23.47.897000000 PM GMT','GMT','') AS D FROM DUAL)
SELECT TO_TIMESTAMP (D, 'DD-MON-YYYY HH.MI.SS.FF AM')
FROM A
Doing something like:
to_timestamp(replace(create_time ,'GMT', null))
relies on your NLS settings, both for the timestamp format - particularly that it has an RR year mask - and the language for the month abbreviation. It would be safer to do:
to_timestamp(replace(create_time, ' GMT', null),
'DD-MON-RR HH.MI.SS.FF AM', 'NLS_DATE_LANGUAGE=ENGLISH')
If the time zone isn't always GMT, but is always a valid and recognised region (not BST, for instance) then you might want to preserve the full date/time including that zone:
to_timestamp_tz(create_time, 'DD-MON-RR HH.MI.SS.FF AM TZR',
'NLS_DATE_LANGUAGE=ENGLISH')
If you want that as a plain timestamp you can cast it, possibly changing to a specific zone first:
cast(to_timestamp_tz(create_time, 'DD-MON-RR HH.MI.SS.FF AM TZR',
'NLS_DATE_LANGUAGE=ENGLISH') as timestamp)
or
cast(to_timestamp_tz(create_time, 'DD-MON-RR HH.MI.SS.FF AM TZR',
'NLS_DATE_LANGUAGE=ENGLISH') at time zone 'Asia/Tokyo' as timestamp)
or normalised to UTC (which won't affect GMT values, of course, as they're essentially the same):
sys_extract_utc(to_timestamp_tz(create_time, 'DD-MON-RR HH.MI.SS.FF AM TZR',
'NLS_DATE_LANGUAGE=ENGLISH'))
db<>fiddle

How to get a date at midnight

I would like to know how to get a date with 00 hour, 00 minutes and 00 seconds.
This
select to_char(sysdate, 'dd/MM/YYYY HH:mm:ss') from dual;
gives the date at the time I asked it
If I don't give any hour :
select to_char(to_date('03/05/2017', 'DD/MM/YYYY'), 'DD/MM/YYYY HH:mm:ss') from dual;
I have a date at noon.
How can I get a date (with sysdate or giving my own date with to_date) at 00:00:00
Thank you
Use TRUNC( date_value, format_model ) and either omit the format model (the default is to truncate to midnight) or use one of the format models 'ddd', 'dd' or 'j':
SELECT TRUNC( SYSDATE ) FROM DUAL;
I dont give any hour :
select to_char(to_date('03/05/2017', 'DD/MM/YYYY'), 'DD/MM/YYYY HH:MI:SS') from dual;
I have a date at noon.
No, you have the date at midnight formatted with a 12-hour clock.
select to_char( to_date('03/05/2017', 'DD/MM/YYYY'), 'DD/MM/YYYY HH:MI:SS PM')
Outputs 03/05/2017 12:00:00 AM
To get a 24-hour clock you need to use HH24 in the format model (rather than HH or HH12 which is a 12-hour clock):
select to_char( to_date('03/05/2017', 'DD/MM/YYYY'), 'DD/MM/YYYY HH24:MI:SS')
Outputs 03/05/2017 00:00:00
mm should be replaced with mi for minutes:
select to_char(to_date('03/05/2017', 'DD/MM/YYYY'), 'DD/MM/YYYY HH:mm:ss') from dual;

Oracle TO_DATE format, symbol for "any character"

In this answer the following query is suggested:
SELECT TO_DATE(
'Thu Nov 24 15:20:52 CET 2016',
'DY MON DD HH24:MI:SS "CET" YYYY'
)
FROM DUAL
¿ there are some character to be used in the format with the meaning of "any" ? I like replace "CET" in the format string by something like "..." or "###", that is:
'DY MON DD HH24:MI:SS ... YYYY'
One example, ¿ which format will accept all the following date strings :
2017-12-31
2017_12_31
2017x12x31
You can extract relevant part by regular expression.
select
TO_DATE(
regexp_substr('Thu Nov 24 15:20:52 CET 2016', '^.+\d{2}:\d{2}:\d{2}')
||regexp_substr('Thu Nov 24 15:20:52 CET 2016', ' \d{4}$'),
'DY MON DD HH24:MI:SS YYYY')
from dual;
or
select
TO_DATE(
REGEXP_REPLACE('Thu Nov 24 15:20:52 CET 2016', '\w+ (\d{4})$', '\1'),
'DY MON DD HH24:MI:SS YYYY')
from dual;
Here's another way using to_timestamp_tz:
WITH sample_data AS (SELECT 'Thu Nov 24 15:20:52 CET 2016' str FROM dual UNION ALL
SELECT 'Fri Dec 11 23:16:39 PST 2015' str FROM dual)
SELECT str,
to_timestamp_tz(str, 'Dy Mon dd hh24:mi:ss TZR yyyy', 'nls_date_language = english') timestamp_at_orig_tz,
CAST(to_timestamp_tz(str, 'Dy Mon dd hh24:mi:ss TZR yyyy', 'nls_date_language = english') AS DATE) dt,
CAST(to_timestamp_tz(str, 'Dy Mon dd hh24:mi:ss TZR yyyy', 'nls_date_language = english') AT TIME ZONE '-5:00' AS DATE) dt_at_utc_minus_5,
cast(sys_extract_utc(to_timestamp_tz(str, 'Dy Mon dd hh24:mi:ss TZR yyyy', 'nls_date_language = english')) AS DATE) dt_at_utc
FROM sample_data;
STR TIMESTAMP_AT_ORIG_TZ DT DT_AT_UTC_MINUS_5 DT_AT_UTC
---------------------------- -------------------------------- ------------------- ------------------- -------------------
Thu Nov 24 15:20:52 CET 2016 24-NOV-16 15.20.52.000000000 CET 24/11/2016 15:20:52 24/11/2016 09:20:52 24/11/2016 14:20:52
Fri Dec 11 23:16:39 PST 2015 11-DEC-15 23.16.39.000000000 PST 11/12/2015 23:16:39 12/12/2015 02:16:39 12/12/2015 07:16:39
I've given a three different ways of converting the timestamp-as-a-string into a date:
Convert the timestamp as is into a date. Note that this will lose any timestamp information you have - useful if you really don't care about timezone information.
Convert the timestamp into a specific timezone (in my example, UTC - 5) before converting it into a date.
Convert the timestamp into UTC before converting it into a date (you can use the previous method to do this, but sys_extract_utc() is a little more self-explanatory, IMHO, which can only aid readability and maintainability!).
With your updated question (which seems to be more around the date part separator, not the timezone), the following should do the trick:
WITH sample_data AS (SELECT '2017-12-31' dt_str FROM dual UNION ALL
SELECT '2017_12_31' dt_str FROM dual UNION ALL
SELECT '2017x12x31' dt_str FROM dual)
SELECT dt_str,
to_date(regexp_replace(dt_str,
'([[:digit:]]{4}).([[:digit:]]{2}).([[:digit:]]{2})',
'\1-\2-\3'), 'yyyy-mm-dd')
FROM sample_data;
DT_STR DT
---------- ----------
2017-12-31 31/12/2017
2017_12_31 31/12/2017
2017x12x31 31/12/2017

specifying a date/time format with timezone

Is it possible to display sysdate in the following format?
Wed Oct 16 15:04:44 MDT 2013
This comes out of unix date format.
echo `date`
EDIT: the latest version is missing timezone information. Also, I am not sure if this is the most elegant solution :
SELECT TO_CHAR (sysdate, 'DY') || ' ' || to_char(sysdate, 'MON DD') || ' ' || to_char(sysdate, 'HH24:MM:SS' ) || ' ' || to_char(sysdate, 'YYYY' )
FROM DUAL ;
sysdate returns value of date data type, which does not contain information about timezone. To be able to display abbreviated version of timezone region you need to operate on values of timestamp with time zone data types and use TZD format element in a date time format mask:
select to_char( cast(sysdate as timestamp with local time zone)
, 'Dy Mon dd hh24:mi:ss TZD yyyy') as res
from dual
Result:
RES
-------------------------------
Thu Oct 17 02:14:00 PDT 2013
Edit
Wed Oct 16:12:0 2013 is what i got
Try to explicitly specify exact time zone region for a session. Because there are might be several time zone regions associated with one offset and oracle wont be able to choose one and returns null. So before executing the query execute alter session set time_zone='<<specify_exact_time_zone_region>>'. For example:
SQL> alter session set time_zone='Canada/Mountain';
Session altered.
SQL> select to_char( cast(sysdate as timestamp with local time zone)
2 , 'Dy Mon dd hh24:mi:ss TZD yyyy') as res
3 from dual;
RES
-------------------------------
Thu Oct 17 02:51:14 MDT 2013

Oracle Time Zones Conversion (using from_tz)

I'm trying to convert a time (date + time) from one time zone to another. In the query below, I'm trying to convert a time from EST ("America/New_York") to PST ("America/Los_Angeles"). The query is partially working; the results:
DATABASE_DATE = 2012-02-13 1:00:00 PM
LOCALTIME (what I get): 2012-02-12 10:00:00 AM.
So the time is good but the date is wrong. It should be 2012-02-13 instead of 2012-02-12.
Am I doing something wrong? Here's my query:
select to_date( to_char( ( from_tz( to_timestamp( DATABASE_DATE
, 'YYYY-MM-DD HH:MI:SS')
,'America/New_York')
at time zone 'America/Los_Angeles')
,'YYYY-MM-DD HH:MI:SS')
,'YYYY-MM-DD HH:MI:SS') as localtime
from table
Thanks
to_timestamp() gets a string (VARCHAR2, CHAR ...) if you try to give it a date, then oracle will convert it to a string according to NLS_DATE_FORMAT which might vary in different environments and return unexpected results (as in this case).
What you should do is use to_char first, so your query can look like this:
select to_date(to_char((from_tz(to_timestamp(to_char(DATABASE_DATE, 'YYYY-MM-DD HH:MI:SS PM'), 'YYYY-MM-DD HH:MI:SS PM') ,'America/New_York')
at time zone 'America/Los_Angeles'),'YYYY-MM-DD HH:MI:SS PM'),'YYYY-MM-DD HH:MI:SS PM') as localtime
from table
UPDATE: if I understand you right then you want something like this:
select to_char((from_tz(to_timestamp(to_char(DATABASE_DATE, 'YYYY-MM-DD HH:MI:SS PM'), 'YYYY-MM-DD HH:MI:SS PM') ,'America/New_York')
at time zone 'America/Los_Angeles'),'YYYY-MM-DD HH:MI:SS PM TZD') as localtime
from table
SELECT TO_CHAR(NEW_TIME(systimestamp,'EST','PST'), 'DD-MON-YY HH24:MI:SS') AS converted_timestamp_column FROM DUAL;
Please try this as well .There is a function for this purpose in oracle
https://docs.oracle.com/cd/B28359_01/olap.111/b28126/dml_functions_2036.htm
select NEW_TIME (TO_DATE ('2011/11/11 01:45', 'yyyy/mm/dd HH24:MI'), 'AST', 'MST') from dual;

Resources