Safe INTERVAL arithmetic - oracle

This query works withour errors
select add_months(date '2011-01-31', 1) from dual;
, while this one:
select date '2011-01-31' + interval '1' month from dual;
returns
ORA-01839: date not valid for month specified
So is there any safe way to add interval using INTERVAL literal?

This follows ANSI-specified behavior1 of adding INTERVALs to dates. This is also documented here:
When interval calculations return a datetime value, the result must be an actual datetime value or the database returns an error. For example, the next two statements return errors:
SELECT TO_DATE('31-AUG-2004','DD-MON-YYYY') + TO_YMINTERVAL('0-1') FROM DUAL;
SELECT TO_DATE('29-FEB-2004','DD-MON-YYYY') + TO_YMINTERVAL('1-0') FROM DUAL;
The function ADD_MONTHS on the other hand will just give you the last day of the month if the resulting month has less days - and I believe this function was created to address this issue.
1 http://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt
b) Arithmetic is performed so as to maintain the integrity of
the datetime data type that is the result of the <datetime
value expression>. This may involve carry from or to the
immediately next more significant <datetime field>. If the
data type of the <datetime value expression> is TIME, then
arithmetic on the HOUR <datetime field> is undertaken modulo
24. If the <interval value expression> or <interval term> is
a year-month interval, then the DAY field of the result is
the same as the DAY field of the <datetime term> or <datetime
value expression>.
c) If, after the preceding step, any <datetime field> of the
result is outside the permissible range of values for the
field or the result is invalid based on the natural rules for
dates and times, then an exception condition is raised: data
exception-datetime field overflow.

Related

Is there a functon similar to DateDiff in MonetDB which can calculate number of weeks between two dates

Consider two dates, "01-Jan-2011' & '01-Oct-2011'.
I wish to calculate number of weeks in between these dates.
I have tried the following:
select extract ( week from ( (current_date+ interval '5' day) - current_date ));
It returns error " no such unary operator 'week(day_interval)'"
I am able to find number of days by using following :
select extract ( day from ( (current_date+ interval '5' day) - current_date ));
the line above returns the output
Is there any way I can achieve the same?
Further, MonetDB considers week from Monday to Sunday(1-7). Is there any way this can be updated/ customised to Sunday to Saturday.
Thanks.
There are a couple of possibilities that I can think of:
select date '2011-10-01' - date '2011-01-01';
results in a INTERVAL DAY value, actually expressed in seconds of the difference, i.e. 23587200.000. This you could divide by (72460*60), i.e. the number of seconds in a week. But it's still an INTERVAL type, not an INTEGER.
Another way is to first convert the date to integers: the number of seconds since "the epoch" (Jan 1, 1970):
select epoch_ms(date '2011-10-01');
This actually give milliseconds since the epoch, so an extra factor of 1000.
This result you can then manipulate to get what you want:
select (epoch_ms(date '2021-02-02') - epoch_ms(date '2020-12-31')) / (7*24*60*60*1000);
This results in a HUGEINT value (if you have 128 bit integers in your system, i.e. anything compiled with GCC or CLANG), so you can convert this to INTEGER:
select cast((epoch_ms(date '2011-10-01') - epoch_ms(date '2011-01-01')) / (7*24*60*60*1000) as integer);

When is systimestamp generated by Oracle

When is the return value of systimestamp function generated in an Oracle sql query? Returned value concerns the moment in which the query is submitted or is ended? And if the function is used in a subquery?
thanks!
NOTE: The discussion below is for SQL only. The behavior of sysdate and systimestamp is different in PL/SQL, the procedural programming language associated with Oracle SQL. Please see Jon Heller's comment below this Answer, and the link he provides in the comment. END OF NOTE
systimestamp is calculated once, at the BEGINNING of the execution of a query. It will have the same value within the query and all its subqueries, no matter how deep and no matter how many times systimestamp is referenced in the query.
For example, the following (intentionally convoluted) query will always return zero:
select sum (abs( date '2000-01-01'
+ ( systimestamp - (select systimestamp from dual) )
- date '2000-01-01'
)
)
from dual
connect by level <= 300000;
NOTE: In a comment below this Answer, the OP points out that - while systimestamp - systimestamp always returns zero, as it should, systimestamp - (systimestamp - 1) returns something like +01 00:00:00.762611. Which is unexpected. So let's explain that.
Date arithmetic with numbers (like 1 to represent one day) is for expressions of date data type - not for timestamps. So for the operation systimestamp - 1, the timestamp is truncated first - only whole seconds are kept, and the fraction of second after the decimal point is discarded.
Then, for an operation of type [timestamp] - [date], Oracle will first cast the date as a timestamp, and then take the difference and return an interval. Which is what the OP saw. The fraction of a second is due to truncation for the systimestamp - 1 calculation, while the first systimestamp is not truncated.
To get the same calculation "done right" (and to see that indeed the result will be exactly +01 00:00:00.000000), we must use the proper data type when we subtract one day from systimestamp. Namely, just like numbers are "what we should use" for date arithmetic, for timestamp arithmetic we must use the interval data type.
select systimestamp - (systimestamp - interval '1' day) from dual
will always return exactly +01 00:00:00.000000.

convert minutes to hh/mi/ss format in oracle query

I want to know the query which converts minutes to the format of hh/mi/ss in Oracle.I 've already seen lot of same questions from many forums but nothing helped me to get the exact result.
The query I used -Select to_char(to_date(mod(100,60),'mi'),'hh/mi/ss') from dual;
But I don't know how to get the hour value.Because mod function returns only the remainder I don't know how to take the quotient part and substitute into the hour field.
I suppose there are two ways of storing "minutes" in an Oracle database - you can either store them in a field whose datatype is INTERVAL DAY TO SECOND, or you can store them in a NUMBER. The simplest case to handle is the INTERVAL - in this case, the TO_CHAR function converts the value to a string of the form SDD HH:MM:SS.FFFFFF, where 'S' is sign ('+' or '-' as intervals can be positive or negative), DD = days, HH= hours, 'MM' = minutes, 'SS' = seconds, and 'FFFFFF' = fractions; thus, to get the HH:MI:SS all we need to do is use the SUBSTR function, as in
SUBSTR(TO_CHAR(I_VAL), 5, 8)
where I_VAL is an INTERVAL DAY TO SECOND value.
If the value to be converted is in a numeric field it gets a bit messy as we have to compute the individual field values, then subtract the previous fields as part of getting the next field. However, since the value stored is in minutes instead of seconds it's not particularly difficult:
create table TST (N_VAL NUMBER,
I_VAL INTERVAL DAY TO SECOND);
INSERT INTO TST(N_VAL, I_VAL) VALUES (666, INTERVAL '666' MINUTE);
SELECT N_VAL,
TRUNC(N_VAL/60) AS HOURS,
N_VAL-(TRUNC(N_VAL/60) * 60) AS MINUTES,
0 AS SECONDS,
TO_CHAR(I_VAL),
SUBSTR(TO_CHAR(I_VAL), 5, 8) AS HMS_FROM_INTERVAL
FROM TST;
SQLFiddle here
Best of luck.

compare 13digit (millisecond) unix timestamp with date in oracle

A database column (VARCHAR2 datatype) stores the date/time as 13 digit (milliseconds
) unixtimestamp format. Now when I want to compare the column with a oracle date (in question), The error thrown as 'invalid number'
I tried both ways,
converting the 13digit number to Date and compare with the date in question like below. The expressions seems valid as they are printed in select query, but if i include in the where part, it throws 'invalid number'
Here 'value' is 13th digit unixtimestamp column of VARCHAR2 datatype.
select
TO_DATE('1970-01-01', 'YYYY-MM-DD') + value/86400000,
TO_DATE('2014-04-21', 'YYYY-MM-DD')
from dummytable
-- where and TO_DATE('1970-01-01', 'YYYY-MM-DD') + value/86400000 > TO_DATE('2014-04-21', 'YYYY-MM-DD')
converting the date in question to 13digit unixtimestamp and comparing with the database column.The expressions seems valid as they are printed in select query, but if i include in the where part, it throws 'invalid number'
.
select
value,
(to_date('2013-04-21', 'YYYY-MM-DD') - to_date('1970-01-01', 'YYYY-MM-DD')) * (1000*24*60*60)
from dummytable
-- where value > ((to_date('2013-04-21', 'YYYY-MM-DD') - to_date('1970-01-01', 'YYYY-MM-DD')) * (1000*24*60*60))
any pointers? Thanks in advance.
[EDIT- 1 day later] I see the problem now. There are some data (other rows) for the 'value' column that are non-numeric. But I have another column say field, where always field='date' return value as 13 digit timestamp. Now I think when 'where' condition executes, although the field='date' is in the condition, it is still validating the other values for 'value' which are non-numeric. Is there a way to avoid this ?
Your code works just fine. The problem is in your data. Some of your values is not a number.
create table test
(value varchar2(13));
insert into test(value) values('2154534689000');
--insert into test(value) values('2 54534689000');
select TO_DATE('1970-01-01', 'YYYY-MM-DD') + value/86400000
from test
where TO_DATE('1970-01-01', 'YYYY-MM-DD') + value/86400000 > TO_DATE('2014-04-21', 'YYYY-MM-DD');
This code works fine. But if you uncommented the second insert, you would get exactly the same invalid number error as you get.
UPD. Allan gave you a nice hint, but i feel that it can be good to explain you a bit about views. The fact that you select from a view CAN make a difference. A view is not stored somewhere physically, when you select from it, it is just "added to your query". And then Oracle Query Optimizer starts working. Among other things, it can change the order in which your where predicates are evaluated.
For example, your the view query can have a line where value is not null and it would normally show only 'good' values. But if your query has a predicate where to_date(value,'ddmmyyyy') > sysdate, Oracle can decide to evaluate your predicate earlier, because Oracle predicts that it would "cut off" more rows, thus making the whole query faster and less momery consuming. Of course, execution will crash because of an attempt to convert a null string to date.
I believe, that Allan in his answer that he gave a link to, gave a great way to solve this problem: "wrapping" your query in a subquery that Oracle can't "unwrap":
select value
from
(select value
from my_view
where rownum > 0)
where to_date(value,'ddmmyyyy') > sysdate
Hope that helps.

using date function to query

I have the following table
Alarm (AlarmID INT, InstalledDate Date)
Given that the alarm need to be replace every 5 years, how do i display all the alarms that is due for replacement in the next 6 months?
I tried the following and there was no result:
SELECT AlarmID
FROM Alarm
WHERE Add_months(InstalledDate, 60)
BETWEEN SYSDATE AND Add_months(SYSDATE, 6);
"I tried the following and there was no result:"
The query you propose looks correct, so perhaps you don't have any ALARMS which are five years old?
"it seems like there is a difference using BETWEEN SYSDATE AND
Add_months(SYSDATE, 6) compare to BETWEEN Add_months(SYSDATE, 6) AND
SYSDATE;"
The BETWEEN operator demands that we pass the two values in a specific order, lower bound then upper bound. So this filter is true:
where date '2012-03-01' between date '2012-01-01' and date '2012-06-01'
whereas this is false:
where date '2012-03-01' between date '2012-06-01' and date '2012-01-01'
Perhaps this seems unfair, but the Oracle documentation makes it clearer by translating the BETWEEN operator into lt and gt statements:
where date '2012-03-01' >= date '2012-01-01'
and date '2012-03-01' <= date '2012-06-01'
If you swap the values of the second and third expressions you'll see why the reversed order returns false.

Resources