ORA-01873: the leading precision of the interval is too small - oracle

When I try to query differences between 2 timestamps in Oracle, the result returns the interval normally.
select NVL2(ERROR_OUT_TS, ERROR_OUT_TS-ERROR_IN_TS, null) from table
or
select interval '8 00:00:10' day to second from dual
But when I try to select rows with greater than some interval, Oracle give me this error.
where ERROR_OUT_TS - ERROR_IN_TS <= '00 00:02:00'
or
where ERROR_OUT_TS - ERROR_IN_TS >= interval '0 00:00:10' day to second
It keeps saying that "the leading precision is too small".
I am trying to return the interval like 0 00:00:00:000
It is working fine for other customers. Only few customers are experiencing it.
How to choose the correct precision?

Try:
select interval '8 00:00:10' day(4) to second(4) from dual;
What it does is that 'day' and 'second' are default 2 digits, this expands them to accept 4. You probably just need 3 though.

Related

Oracle Time Range Query

I need to write a query which will do a date lookup by the most completed quarter hour.
So, if the current time is 5:35, then the criteria would be 5:15 - 5:30. If the time is 5:46, then 5:30 - 5:45, if the time is 6:02, then 5:45 - 6:00.
Not sure how to easily do this.
Something like
with cqh (dt) as (
select trunc(sysdate, 'hh') +
trunc(extract(minute from systimestamp) / 15) * interval '15' minute
from dual
)
select [_data_] from [_your_table_] cross join cqh
where [_date_column_] >= cqh.dt - interval '15' minute
and [_date_column_] < cqh.dt
;
The subquery calculates the most recently completed quarter-hour. It first truncates SYSDATE to the beginning of the current hour. Then we add a multiple of 15 minutes - the multiplier is 0, 1, 2 or 3, depending on the minute component of SYSDATE. Alas, EXTRACT(MINUTE FROM ...) only works on timestamps, so I had to use SYSTIMESTAMP there instead of SYSDATE, but other than that, the computation should be pretty obvious.
Then cross-join whatever else gives you "the data" to this small helper view, to use the DT value calculated in it.

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);

Oracle OR statement

I need to select records where the ACTUAL_END_DATE is null or has a date that is no older than 90 days at the point of running the query.
This is what I have tried using but it is returning too many records, it seems to be ignoring the OR clause
CU.ACCOUNT_TYPE = 'C' AND ((C.ACTUAL_END_DATE IS NULL) OR (C.ACTUAL_END_DATE <= (SYSDATE + 90)))
Any help would be greatly appreciated.
Keith
It would be easier if you gave an example of a record that should not have been returned, but was.
That being said:
"no older than 90 days at the point of running the query."
Would indicate you want items where SYSDATE - ACTUAL_END_DATE is /less than or equal/ to 90, not greater than or equal to 90.
As written, presuming no end dates are in the future, you are getting all records that have already ended (since any end date will be less than or equal to SYSDATE, and the 90 becomes superfluous).
If this is the case, what you want is:
CU.ACCOUNT_TYPE = 'C' AND ((C.ACTUAL_END_DATE IS NULL) OR (C.ACTUAL_END_DATE >= (SYSDATE - 90)))

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.

Can can someone explain me the query in the function

CREATE OR REPLACE FUNCTION exlude_weekends (p_date_start DATE,
p_date_end DATE)
RETURN NUMBER
AS
l_no_of_days NUMBER := NULL;
BEGIN
SELECT COUNT ( * ) INTO l_no_of_days
FROM (SELECT date_extraction, TO_CHAR (date_extraction, 'DAY')
FROM (SELECT TO_DATE(p_date_start,'DD-MON-RRRR')
+ LEVEL - 1 date_extraction FROM DUAL CONNECT BY LEVEL <
(TO_DATE (p_date_end, 'DD-MON-RRRR')- TO_DATE (p_date_start,'DD-MON-RRRR'))+ 2)
WHERE TRIM (TO_CHAR (date_extraction, 'DAY')) NOT IN ('SATURDAY', 'SUNDAY'));
RETURN l_no_of_days;
EXCEPTION
WHEN OTHERS
THEN
RETURN 0;
END exlude_weekends;
As you mention in the comments, the key of the function is the hierarchical sub-query:
SELECT TO_DATE(p_date_start,'DD-MON-RRRR') + LEVEL - 1 date_extraction
FROM DUAL
CONNECT BY LEVEL <
(TO_DATE(p_date_end,'DD-MON-RRRR')-
TO_DATE(p_date_start,'DD-MON-RRRR'))+ 2
Hierarchical queries try to traverse a tree (CONNECT BY clause specifies how parents and children are related). In this example we find a tricky use (or abuse) of the connect by.
This sub-query generates date from p_date_start to p_date_end (both inclusive). How it does it?
Note that the expression being compare with LEVEL in the CONNECT BY is a constant, and it is the number of days between start and the day after the end date (why the day after the end date? because it is using < and the day after the end date is the first day out of the interval):
(TO_DATE(p_date_end,'DD-MON-RRRR')-TO_DATE(p_date_start,'DD-MON-RRRR'))+2
The select get the DUAL row (it has only one row) this row has LEVEL 1 (hierarchical query use the pseudo-column LEVEL to indicate the depth from the root where it started to evaluate).
The CONNECT BY checks that this level (1) is in the range of days to be generated.
Evaluates the expression:
TO_DATE(p_date_start,'DD-MON-RRRR') + LEVEL - 1
This is the start date plus the level minus one: this is, the start date.
Now a new cycle in hierarchical evaluation starts: the row generated in the previous cycle (the start date) is evaluated again (the new row will have level 2).
If it is in the range of days to be generated (controlled by the CONNECT BY clause) a new date is generated (the day after the start date).
A new cycle start (level 3)....
And the process iterates until LEVEL is greater than the number of days to be generated (which is the same than the number of levels required to iterate from the start date to the end date).
The outer queries in the function only filter SATURDAYS and SUNDAYS and count the remaining days.
Although oracle is very efficient evaluating this query, this function uses a brute force solution.
A more elegant and mathematical solution can be used (with no iterations). We have an equation that computes the number of a particular day of week between two dates:
TRUNC(( END – START – DAYOFWEEK(END-DAYOFWEEKTOBECOUNTED) + 8) / 7)
where DAYOFWEEK is a function that returns 0-6 (0 Sunday, 1 Monday ... 6 Saturday). And DAYOFWEEKTOBECOUNTED is the number of the day to be counted in the same format.
Note that TO_CHAR(date, 'd') returns the day of week in 1..7 format we must rectify to 0..6 format (In my region monday is the first day of week, so i get sunday as 0 and saturday as 6 with the mod function as follows):
MOD(TO_NUMBER(TO_CHAR(p_date_end, 'd')), 7)
Finally we want the number of days in the interval minus the number of sundays (day 0) and saturdays (day 6). So the final procedure with the mathematical approach will be:
CREATE OR REPLACE FUNCTION exlude_weekends (p_date_start DATE,
p_date_end DATE)
RETURN NUMBER
AS
l_no_of_days NUMBER := NULL;
BEGIN
SELECT TRUNC(p_date_end - p_date_start) + 1 -
( TRUNC((p_date_end - p_date_start -
MOD(to_number(to_char(p_date_end - 0, 'd')), 7)+8)/7)
+ TRUNC((p_date_end - p_date_start -
MOD(to_number(to_char(p_date_end - 6, 'd')), 7)+8)/7)
)
INTO l_no_of_days
FROM DUAL;
RETURN l_no_of_days;
EXCEPTION
WHEN OTHERS
THEN
RETURN 0;
END exlude_weekends;

Resources