Confusion using to_date function to confirm date format - oracle

select to_date('07/09/14','yyyy-mm-dd') from dual;
is returning 14-SEP-07
I was expecting it to thrown an exception as the date and the format requested are not the same. Secondly we have slashes in the input date and hypen in the format.
Can someone tell me how to confirm if the input value is of the provided format.

to_date is relatively liberal in attempting to convert the input string using the provided format mask. It generally doesn't concern itself with the specific separator character in the string or in the format mask-- your string could use dashes or slashes or, heck, asterixes if you wanted. Of course, that can mean that you get unexpected results. In this case, for example, the date that is created is in the year 7 (i.e. 2007 years ago). That is a valid date in Oracle but it is highly unlikely to be the date you expect unless you're storing data about ancient Rome.
What, exactly, does it mean to you to "confirm if the input value is of the provided format"? Depending on what you are looking for, you may want to use regular expressions.

REGEXP_LIKE can somewhat do this. Note, this is basic, since it would accept values like 2014-0-32. However, those insane values would fail in whatever you do next in your code, such as to_date().
SELECT 'Yes, valid boss.' is_valid FROM DUAL
WHERE regexp_like('07/09/14','^[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}$');
no rows selected
.
SELECT 'Yes, valid boss.' is_valid FROM DUAL
WHERE regexp_like('2014-07-09','^[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}$');
IS_VALID
----------------
Yes, valid boss.
.
SELECT 'Yes, valid boss.' is_valid FROM DUAL
WHERE regexp_like('2014-7-9','^[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}$');
IS_VALID
----------------
Yes, valid boss.
EDIT: and if you're into PL/SQL, you can do a regex match and throw your own exception ...
DECLARE
v_is_valid INTEGER;
BEGIN
SELECT count(*) INTO v_is_valid FROM DUAL
WHERE regexp_like('07/09/14','^[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}$');
IF v_is_valid = 0 THEN
raise_application_error (-20400, 'Exception: date was given in the wrong format.');
END IF;
END;
/
*
ORA-20400: Exception: date was given in the wrong format.

TO_DATE() only takes a string and convert it to a date/time in respect to the given format your provided. If it can't find the exact format, it will do its best to determine what could it be. Given your 'yyyy-mm-dd' format, Oracle deduces that the 07 is the year, and the 14 is the day.
You have to write your own function to throw an expected exception or the like to handle the issue, if there is any based on your functional requirements.
So when you select the resulting date, the format that is displayed is actually based on your NLS_DATE_FORMAT system parameter, which gives your resulting date.

Related

Oracle Varchar2 column with multiple date formats

I have a table (my table ) with a varchar2 column (my_column) , this column inculdes data that may be dates, in almost every known date format. like
dd/mm/yyyy
mm/dd/yyyy
ddMONyyyy
ddmmyyyyy
yymmdd
mm.dd.yy
dd/mm/yyyy hh:24:ss
mm-dd-yyyy
and so many .
Also it could contains a free text data that may be numbers or text.
I need the possible fastest way to retrieve it as a date , and if it's cannot match any date format to retrieve null
Said that this is really a bad and dangerous way to store dates, you could try with something like the following:
select myColumn,
coalesce(
to_date(myColumn default null on conversion error, 'dd/mm/yyyy' ),
to_date(myColumn default null on conversion error, 'yyyy mm dd' ),
... /* all the known formats */
to_date(myColumn default null on conversion error ) /* dangerous */
)
from myTable
Here I would list all the possiblly expected formats, in the right order and trying to avoid the to_date without format mask, which can be really dangerous and give you unexpected results.
The simple answer is: You cannot convert the strings to dates.
You say that possible formats include 'dd/mm/yyyy' and 'mm/dd/yyyy'. So what date would '01/02/2000' be? February 1 or January 2? You cannot know this, so a conversion is impossible.
Having said that, you may want to write a stored procedure where you parse the text yourself and only convert unambiguous strings.
E.g.: Is there a substring containing colons? Then this may be the time part. In the rest of the string: Is there a four-digit number? Then this may be the year. And so on until you are either 100% sure what date/time this is or you cannot unambiguously determine the date (in which case you want to return null).
Example: '13/01/21' looks very much like January 13, 2021. Or do you allow the year 2013 in your table or even 2001 or even 1921 or ...? If you know there can be no year less then, say, 2019 in the table and no year greater than 2021, then the year must be 2021. 13 cannot be the month, so it must be the day, which leaves 01 for the month. If you cannot rule out all other options, then you would have to return null.

Oracle: Why does to_date() accept 2 digit year when I have 4 digits in format string? - and how to enforce 4 digits?

I want to check a date for correctness. (Let's not talk about the fact, that the date is stored in a varchar please ...)
Dates are stored like DDMMYYYY so for instance 24031950 is a correct date. 240319 is not.
So I do this, when the call works, it's a correct date:
select to_date('24031950','DDMMYYYY') from dual;
But unfortunately this also does not return an error:
select to_date('240319','DDMMYYYY') from dual (why?);
But it's interesting, that this one does not work:
select to_date('190324','YYYYMMDD') from dual;
So, how to enforce a check o 4 digit year with the given format mask?
Thanks!
Quoting the docs:
Oracle Database converts strings to dates with some flexibility. [...]
And I believe that flexibility is what you are seeing. You can turn it off with the fx modifier:
FX: Requires exact matching between the character data and the format model
select to_date('240319','fxDDMMYYYY') from dual;
Gives an ORA-01862 error.
Just an additional note to Mat's answer. fx acts like a switch in the string.
For example TO_DATE('2019-11-5','fxYYYY-MM-DD') gives an ORA-01862 error because exact matching applies for the entire string. If you need exact match only for parts of the string, then use for example
TO_DATE('2019-11-5','fxYYYY-MM-fxDD')
In this case YYYY-MM- has to match exactly, whereas DD applies flexible (or lazy) match.
To check whether it is in correct format without exceptions you can also use regex functions.
One possible way would to be check if the string contains 8 digits:
select REGEXP_INSTR ('24031950', '[0-9]{8}') from dual;
1
select REGEXP_INSTR ('240350', '[0-9]{8}') from dual;
0

Oracle a non-numeric character was found where a numeric was expected

I have problem with some thing input multiple date through prompt
my query something like
select something
from
something
where
to_date (bus.TDATE) not in (:didb1)
I want to input like '12-Jun-2016','11-Jun-2016'
i am doing php oracle,
My code following:
select bus.*,con.* from
BusinessPayment bus,
ConsumerPayment con
where bus.TDATE=con.TDATE
and to_date (bus.TDATE) not in (:didbv)
');
$bscode1='11-June-2016','10-June-2016','09-June-2016';
oci_bind_by_name($stid, ':didbv', $bscode1);
oci_execute($stid);
You can't use a single bind variable to represent a list of values in an in() clause. Bind variables don't contain multiple values; the value you bind is seen as a single string value. So you're effectively doing:
where
to_date (bus.TDATE) not in (q'['11-June-2016','10-June-2016','09-June-2016']')
If TDATE is already a date type - which it hopefully is, and the joins suggest it might be - then you should not be passing that through to_date() - you are doing an implicit conversion from date to string, then a semi-explicit conversion back to a date, both using your session's NLS_DATE_FORMAT. That is either doing unnecessary work, or losing resolution, depending on your format model and whether the dates have times. If the intention is to ignore the time part of the value then there are better ways to do that.
Anyway, since that is a date now whatever type it was before, the right had side of that filter will also be converted to a date, so you're doing:
where
to_date (bus.TDATE) not in (to_date(q'['11-June-2016','10-June-2016','09-June-2016']'))
... and it's that which is throwing the exception, whatever your NLS settings are. Even if you passed a single value it would error because of the embedded single quotes. You can replicate that with:
select to_date(q'['11-June-2016','10-June-2016','09-June-2016']') from dual;
which also gets ORA-01858: a non-numeric character was found where a numeric was expected.
Ideally you would pass the individual date strings as elements of an array, but you can also work around this with an XML hack. You can pass a single string containing comma-delimited values to XMLTable(), and it will split it in to rows:
select * from xmltable(q'['11-June-2016','10-June-2016','09-June-2016']');
COLUMN_VALUE
--------------------------------------------------------------------------------
11-June-2016
10-June-2016
09-June-2016
Then you can convert each of those values to a date:
select to_date(column_value, 'DD-Month-YYYY')
from xmltable(q'['11-June-2016','10-June-2016','09-June-2016']')
and use that in a subquery or a join:
select * from dual
where trunc(sysdate) not in (select to_date(column_value, 'DD-Month-YYYY')
from xmltable(q'['11-June-2016','10-June-2016','09-June-2016']'));
And that works with a string bind variable, so your code would be:
select bus.*,con.*
from BusinessPayment bus
join ConsumerPayment con
on bus.TDATE=con.TDATE
where bus.TDATE not in (select to_date(column_value, 'DD-Month-YYYY')
from xmltable(:didbv));
I've modified this to use ANSI join syntax, which isn't relevant but there's rarely a reason to use the ancient syntax you have. If TDATE does include the time you'll need to modify it a bit - truncating is simplest but might affect performance.
Or you could join:
select bus.*,con.*
from xmltable(:didbv) x
join BusinessPayment bus
on bus.TDATE >= to_date(x.column_value, 'DD-Month-YYYY')
and bus.TDATE < to_date(x.column_value, 'DD-Month-YYYY') + 1
join ConsumerPayment con
on bus.TDATE=con.TDATE
In this version TDATE can have a time, and it matches any time on the days specified in the bind variable.
It would be better to supply the dates in a format that doesn't have the month name in it, as that could give you problems when run in different sessions or locales. If you know the PHP side will always be English you could force that to be recognised:
on bus.TDATE >= to_date(x.column_value, 'DD-Month-YYYY', #NLS_DATE_LANGUAGE=ENGLISH')
but if you can change the PHP format to use month numbers, and change the Oracle format model to match, that would be a bit simpler.

Oracle - Date Format

I need your assistance with converting Oracle dates.
I have a column that stores dates like this 20150731 00:00:34.220. However, I would like to show the column like this 20150731 but when I run a simple select statement to test output I get the following error.
select TO_DATE('20150731 00:00:34.550','YYYYMMDD')
from dual
Error
ORA-01830: date format picture ends before converting entire input string
This query
select TO_DATE('20150731 00:00:34.550','YYYYMMDD')
from dual
leads to error
ORA-01830: date format picture ends before converting entire input string
because you pass string with length 22 characters, but at the same time you pass date format with 8 characters, which obviously doesn't correspond to string. You should write the query as
select to_timestamp('20150731 00:00:34.550','yyyymmdd hh24:mi:ss.ff3')
from dual
As for your table, since you have varchar2 column with dates, you have to take care about table content. Query requires exect matching of the source string and date format.
If you want to show only date without time and you don't need to process this string as date, you can make just
select substr('20150731 00:00:34.550', 1, 8)
from dual
What is the data type of the column? If it is DATE (as it should be) then not it is not stored in the format you say. It is stored in an internal binary format. You would/should use the to_char function to DISPLAY it in whatever format you choose. If you do not use the to_char function, it will be displayed in the format specified by NLS_DATE_FORMAT, which can be specified at several locations.
As for your example, you passed a string format of yyyymmd hh:mi:ss.fff', but you provided a description mask of only YYYYMMDD. It doesn't know what to do with time component. In addition to that when you SELECT TO_DATE, oracle also has to do an implied TO_CHAR to convert it back to a string for display purposes.
In addition, you provided your to_date with a character string that included fractions of seconds. A DATE data type only resolves to seconds. If you need fractional seconds, you need to use TIMESTAMP, not DATE.
If your column is a varchar and you need a date output:
select TO_DATE(substr('20150731 00:00:34.550', 1, 8),'YYYYMMDD') from dual
If it's in a date format and you need a string output:
select to_char(your_column, 'YYYYMMDD') from your_table
Is that being stored in an Oracle datetime column? If not, you may have to do some manipulation to get it into a DD-MON-YYYY format. If it is being stored as a text string you could use SUBSTR( Date_field, Start_Position, Length) to get the first 8 characters. check out this link SUBSTR
Working on the assumption that you're not trying to change the value in the column, and are just trying to show it in the YYYYMMDD format -
As mentioned by a_horse_with_no_name, you'll just need to convert it to a character string. In this example I used systimestamp as my date:
SELECT TO_CHAR(systimestamp,'YYYYMMDD') FROM DUAL
Result:
20160121
That should give you the YYYYMMDD format you want to display.

date format with inconsistant data Oracle SQL

I am have a freetext column which a date is input to.
is there a way i can force the output to display the same?
for example some people are entering '2/12/15' others are entering '02/12/2015'
how i can i get the output to pick up the dates and change them to a consistant format?
no idea where to start. I have tried "To_char" but it says 'invalid number'
Actually I believe you want to_date( ):
select to_date('2/12/15', 'mm/dd/rrrr')
from dual;
The 'rrrr' for year will format the year correctly. See date formats here: https://docs.oracle.com/cd/B28359_01/server.111/b28286/sql_elements004.htm
Your best approach here may be to use a PL/SQL function that accepts the string and then attempts a series of date conversions on it with different date format pictures ('DD/MM/YY', 'DD/MM/YYYY' etc) until it finds one that does not raise an exception.
This won't work if some people enter DD/MM/YYYY and others enter MM/DD/YYYY, but will appear to in some cases.

Resources