Oracle - Converting Date value TO_CHAR() - oracle

Ok, so I want to convert a date value to properly format my date for comparison, however, will converting my "DATE" data type to char affect indexing when comparing on that field? Can I resolve this by doing to_date(tochar())? Any advice on this would be greatly appreciated Thanks!
EDIT - Sorry for the lack of specifics.... basically I need to eliminate the time stamp from my date, I used the following and it appears to work TO_DATE(TO_CHAR, 'YYYY-MM-DD'),'YYYY-MM-DD'), mind you I don't know if this is good practice or not, but at least (or so I think) now it's comparing a DATE against a DATE, and not a string.

If you are doing a comparison, you should not be converting the date to a string. You should be comparing to another date. Otherwise, Oracle won't be able to use a non function-based index on the date column.
In general, that is, you're much better off coding
WHERE some_indexed_date_column = to_date( :string_bind_variable,
<<format mask>> )
rather than
WHERE to_char( some_indexed_date_column,
<<format mask>> ) = :string_bind_variable
Of course, if your bind variable can be a DATE rather than a VARCHAR2, that's even better because then you don't have to do any data type conversion and the optimizer has a much easier time of estimating cardinalities.
If you are trying to do some manipulation of the date-- for example, if you want to compare the day portion while omitting the time portion of the date-- you may want to use function-based indexes. For example, if you wanted to find all the rows that were created some time today
WHERE trunc( some_date_column ) = date '2011-11-04'
you could either create a function-based index on the date column
CREATE INDEX idx_trunc_dt
ON table_name( trunc( some_date_column ) )
or you could rewrite the query to do something like
WHERE some_date_column >= date '2011-11-04'
AND some_date_column < date '2011-11-05'

You should compare dates as dates, not as strings. If comparing a date to a string, convert the string to a date to compare.

IMO, you should convert your comparison value to the format stored in the database. Otherwise, you will need to create a function based index on the DATE column to take advantage of indexing. So, if you have an input character date of, say, 11/4/2011, you could compare it in a where clause thusly:
SELECT ...
FROM your_table
WHERE the_date_column = TO_DATE('11/4/2011','MM/DD/YYYY');

Related

Query not filtering with date in Oracle

There are records in table for particular date. But when I query with that value, I am unable to filter the records.
select * from TBL_IPCOLO_BILLING_MST
where LAST_UPDATED_DATE = '03-09-21';
The dates are in dd-mm-yy format.
To the answer by Valeriia Sharak, I would just add a few things since your question is tagged Oracle. I was going to add this as a comment to her answer, but it's too long.
First, it is bad practice to compare dates to strings. Your query, for example, would not even execute for me -- it would end with ORA-01843: not a valid month. That is because Oracle must do an implicit type conversion to convert your string "03-09-21" to a date and it uses the current NLS_DATE_FORMAT setting to do that (which in my system happens to be DD-MON-YYYY).
Second, as was pointed out, your comparison is probably not matching rows due LAST_UPDATED_DATE having hours, minutes, and seconds. But a more performant solution for that might be:
...
WHERE last_update_date >= TO_DATE('03-09-21','DD-MM-YY')
AND last_update_date < TO_DATE('04-09-21','DD-MM-YY')
This makes the comparison without wrapping last_update_date in a TRUNC() function. This could perform better in either of the following circumstances:
If there is an index on last_update_date that would be useful in your query
If the table with last_update_date is large and is being joined to other tables (because it makes it easier for Oracle to estimate the number of rows from your table that are inputs to the join).
Your column might contain hours and seconds, but they can be hidden.
So when you filter on the date, oracle implicitly adds time to the date. So basically you are filtering on '03-09-21 00:00:00'
Try to trunc your column:
select * from TBL_IPCOLO_BILLING_MST
where trunc(LAST_UPDATED_DATE) = '03-09-21';
Hope, I understood your question correctly.
Oracle docs

How to make a constraint on DATE FORMAT in ORACLE

say my usual Date format is '14-jan-2019' and i want my date to only be accepted as 'YYYY-MM-DD' how do i do that? and can i change jan to an actual number?
In my opinion, the right / correct way to do that is to declare your date column (or variable or whatever it is) as DATE, e.g.
create table test (date_column date);
or
declare
l_date_variable date;
begin
...
Doing so, you'd let the database take care about valid values.
You'd then be able to enter data any way you want, using any valid date format mask, e.g.
to_date('06.01.2020', 'dd.mm.yyyy')
date '2020-01-06'
to_date('2020-06-01', 'yyyy-dd-mm')
etc. - all those values would be valid.
A DATE data type has no format - it is stored internally as 7-bytes representing year (2 bytes) and month, day, hour, minute and second (1 byte each).
'14-JAN-2019' is not a date - it is a text literal.
If you want to store date values then use a DATE data type.
If you want to only accept strings of a specific format as input then in whatever user interface you use to talk to the database then accept only that format. If you are converting strings to DATEs and wanting an exact format then you can use:
TO_DATE( '2019-01-14', 'fxYYYY-MM-DD' )
Note: the fx format model will mean that the exact format is expected and the typical string-to-date conversion rules will not be applied.

How to solve the error of this select writing in oracle?

I want to do the select from the range 06/01/2019 to 09/01/2019, but the program returns other ranges, like show the image.
Remember that my date field in the database is varchar2, and I store it in the format dd / MM / yyyy
Expanding #user7294900's answer, as you're storing your 'date' value as a string, you have to convert both the column value and your target-range values from strings to actual dates.
If you do:
WHERE DATA_TRANSACAO BETWEEN '06/01/2019' AND '09/01/2019'
then you are doing string comparisons between the column value and those literals, and as a string anything starting with '07' or '08' (and most starting with '06') will be considered part of that range. Oracle is treating them purely as strings, it doesn't know or care that you have the notion of days and months, or that you consider the order to be something different.
If you're really stuck with strings then you need to compare both sides of the comparison to dates:
WHERE TO_DATE(DATA_TRANSACAO, 'DD/MM/YYYY') BETWEEN TO_DATE('06/01/2019', 'DD/MM/YYYY')
AND TO_DATE('09/01/2019', 'DD/MM/YYYY')
or with ANSI literals:
WHERE TO_DATE(DATA_TRANSACAO, 'DD/MM/YYYY') BETWEEN DATE '2019-06-01' AND DATE '2019-01-09'
or (though this is more useful if there can be non-midnight times, which isn't the case in your data):
WHERE TO_DATE(DATA_TRANSACAO, 'DD/MM/YYYY') >= DATE '2019-06-01'
AND TO_DATE(DATA_TRANSACAO, 'DD/MM/YYYY') < DATE '2019-01-10' -- notice one day later
You should also be aware that having to convert the string value for every row in your data before you can compare it means that if you have a simple index on that column it cannot be used, and a full table scan will be done even for a tiny target date range. (You can add a function-based index to speed this up, but that's a sticking plaster approach.)
This kind of thing is why you should be using the correct data type. There is also no need to store the date and time parts separately, and that makes other types of comparison and data extract harder and more complicated than it needs to be too.
I would really recommend that you revisit your data model and change from separate strings for DATA_TRANSACAO and HORA_TRANSACAO to a single DATE column which contains the whole date/time value as the proper data type. You'll then be able to use the simpler form, with no conversion of your table data, of something like (with a made-up combined column name):
WHERE DATAHORA_TRANSACAO >= DATE '2019-06-01'
AND DATAHORA_TRANSACAO < DATE '2019-01-10' -- notice one day later
If you expect 06/08 values to represent days, be explicit:
BETWEEN TO_DATE('06/01/2019','DD/MM/YYYY')
AND TO_DATE('09/01/2019','DD/MM/YYYY')
Currently it return 06/08 values as months, In general when compare date column use dates and not strings
I am strongly ENCOURAGING you to use to_date to avoid ambiguities (compare strings to strings, numbers to numbers, dates to dates -- don't RISK confusion comparing strings to numbers, dates to strings).

Varchar to Timestamp but varchar data is yyyy-mm-dd-hh:mi:ss:ff format

My source is from Oracle and the col1 is varchar2(26) but the value looks like YYYY-MM-DD-hh:mi:ss:ff (Sample rec: 2014-08-15-02.03.34.979946).
I have to extract only 6 months records based on COL1. Since there is a hypen between date part and time part - i could not consider as timestamp. Is there any idea how to have this as timestamp to lookup only 6 months data.
If it is possible at all, fix the data first. Storing timestamps in string data type is terrible. How do you know you don't have a time like 25:30:00 in the strings? Or a date like February 30? Besides, you can't really use an index on that column (so queries will be very slow), you will have to write a lot of code whenever referencing that column, etc.
Anyway - to deal with the immediate problem, use TO_TIMESTAMP(), exactly with the format model you show in your post - including the dash between the date part and the time part. Something like this:
select case when to_timestamp('2014-08-15-02.03.34.979946', 'YYYY-MM-DD-HH24:MI:SS.FF')
>= systimestamp - interval '6' month
then 'TRUE' else 'FALSE' end
as result
from dual;
RESULT
------
FALSE
EDIT: As Alex Poole points out (correctly as always) in a Comment below this Answer, interval arithmetic won't work correctly in all cases. It is better, than, to use something like
cast ( timestamp (...., format-model) as date ) <= add_months (sysdate, -6).
Maybe something like this will do:
select *
from your_table
where to_date(substr(col1,1,19),'yyyy-mm-dd-HH24.MI.SS') between add_months(sysdate,-6) and sysdate;
Assuming all the data format in col1 is always the same.
Also note that I used HH24 for hour segment, however could be not your case.
You can include the dash in your format model, as #mathguy showed, to convert your string to a timestamp:
select to_timestamp('2014-08-15-02.03.34.979946', 'YYYY-MM-DD-HH24:MI:SS.FF') from dual;
TO_TIMESTAMP('2014-08-15-02.
----------------------------
15-AUG-14 02.03.34.979946000
although unless you explicitly tell it not to be via the FX modifier, Oracle is flexible enough to allow a dash even if the model has a space (see the text below this table in the documentation:
select to_timestamp('2014-08-15-02.03.34.979946', 'YYYY-MM-DD HH24:MI:SS.FF') from dual;
TO_TIMESTAMP('2014-08-15-02.
----------------------------
15-AUG-14 02.03.34.979946000
However, converting all of the values in your col1 column and then comparing them may be a lot of work, and will prevent any index on that string column being used. Given the format, you can convert your date range to string instead, and use string comparison; e.g. to find everything in the six months up to midnight this morning:
select col1 -- or whichever columns you need
from your_table
where col1 >= to_char(cast(add_months(trunc(sysdate), -6) as timestamp), 'YYYY-MM-DD-HH24:MI:SS.FF6')
and col1 < to_char(cast(trunc(sysdate) as timestamp), 'YYYY-MM-DD-HH24:MI:SS.FF6');
or since the time part can be fixed for that example, you can use character literals instead of casting:
select col1 -- or whichever columns you need
from your_table
where col1 >= to_char(add_months(sysdate, -6), 'YYYY-MM-DD"-00:00:00.000000"')
and col1 < to_char(sysdate, 'YYYY-MM-DD"-00:00:00.000000"');
Of course, storing data in the correct native data type would be a much better solution. Any other solution only works at all if your string data actually contains what you think, and the data is all sane (or as sane as it can be in the wrong data type).

Oracle date comparison as string and as date

1) Why is that this doesn't works
select * from table where trunc(field1)=to_date('25-AUG-15','DD-MON-YY');
select * from table where trunc(field1)=to_date('25/Aug/15','DD/MON/YY');
row is returned in above cases.
So, does this mean that no matter what format the date is there in field1, if it is the valid date and matches with 25th August, it will be returned ( it won't care what format specifier we specify at the right side of the query i.e. DD-MON-YY or DD/MON/YY or anything else) ?
2) but comparsion as string exactly works:
select * from table where to_char(field1)=to_char(to_date
('25/AUG/15','DD/MON/YY'), 'DD/MON/YY');
no row is returned as the comparison is performed exactly.
I have field1 as '25-AUG-15' ( although it can be viewed differently doing alter session NLS_DATE_FORMAT...)
field1 is of DATE type
Any help in understanding this is appreciated specifically with respect to point 1
The DATE data type does not have format -- it's simply a number. So, a DATE 25-Aug-2015 is the same as DATE 25/AUG/15, as well as DATE 2015-08-15, because it's the same DATE.
Strings, on the other hand, are collections of characters, so '25-Aug-2015' is obviously different from '25/AUG/15'.
In the first example you are comparing DATE values. In the second example you are comparing strings.
So you have a field of type DATE with value of The 25th of August 2015,
but it could be visualized in different ways, what in fact is named format.
The DATE has format!
The DATE has implicit format defined by Oracle, in your case it is DD-MON-YY, because you see your field as 25-AUG-15.
You can select your data without TO_DATE conversion, just matching this default format like this:
select * from table where trunc(field1)='25-AUG-15';
In fact, it's not recommended, because if someone will change the default format, Oracle will not be able to understand that you are going to tell him a DATE.
So the to_date conversion in this case:
select * from table where
trunc(field1)=to_date('25/AUG/15','DD/MON/YY');
is used to specify that you wanna tell to Oracle a DATE type with value of 25th of August 2015, using a diffrent format, specified as second parameter. (DD/MM/YY in this case).

Resources