Loop through date range - oracle

How to loop Oracle query through the date? I have to put variable in 4 place. My query start with WITH AS, so I can't use Oracle SQL Loop through Date Range solution.
I also can't create temporary table.
Here is my attempt:
WITH d
AS (
SELECT DATE'2015-06-22' + LEVEL - 1 AS current_d
FROM dual
CONNECT BY DATE'2015-06-22' + LEVEL - 1 < DATE'2015-10-04'
),
OrderReserve
AS (
SELECT cvwarehouseid
,lproductid
,SUM(lqty) lqty
FROM ABBICS.iOrdPrdQtyDate
GROUP BY cvwarehouseid
,lproductid
)
SELECT
...
WHERE IORDREFILL.DNCONFIRMEDDELDATE < CAST(TO_CHAR(d.current_d , 'YYYYMMDD') AS NUMBER(38))
...

If I understand you correctly, you assume that you can only use 1 inline table per query. That is not true, you can use multiple inline tables and expand the existing WITH clause with another to loop through dates:
with OrderReserve as (
SELECT cvwarehouseid
,lproductid
,SUM(lqty) lqty
FROM ABBICS.iOrdPrdQtyDate
GROUP BY cvwarehouseid
,lproductid
), date_range as (
select sysdate+level
from dual
connect by level <= 30
)
select *
from OrderReserve, date_range
... -- expand with date_range as you see fit
;

Related

same query in procedure take different time

i have a same code in procedure with only 1 different (date variable).
in my code i have 27 p_dt variable. Code below is not full.
when i run procedure with p_dt it cost more than 10hours but when i write to_date('01.01.2020','dd.mm.yyyy') instead p_dt it cost 300 sec
create or replace ru.t_maha(p_dt in date default trunc(sysdate) -1) as
begin
delete from t_maha_1
where dt = to_date(add_months(trunc(p_dt,'MONTH'),-1),'dd.mm.yyyy');
commit;
insert into t_maha_1
with scheta_Snt as (
select
inn,
add_months(trunc(p_dt,'MONTH'),-1) || last_Day(add_months(trunc(p_dt,'MONTH'),-1)) interval,
sum(case when t.dt_open between add_months(trunc(p_dt,'MONTH'),-1) and last_Day(add_months(trunc(p_dt,'MONTH'),-1)) then value_nat end ) scheta_snt
from fct_Carry t
)
select * from scheta_snt
join scheta_pop (same subquery but for another calculate)
join dep_snt (same subquery but for another calculate)
join dep_pop (same subquery but for another calculate)

How to get minimum unused number from a column in Oracle and Linq?

I have a column named voucher_number. The data in this column looks like
1, 2
I want a query (in oracle and linq as well) to return 0,3,4,5,6,7,8,9,10
Note: i am taking range (0 to 10 )as a parameter parameter from screen(aspx page)
You can use MINUS operator as following:
Select voucher_num from
(
(Select level - 1 as voucher_num from dual
Connect by level <= 11)
Minus
(Select voucher_number from your_table)
)
Order by voucher_num;
Cheers!!

How to get monthly periods between a date range in Oracle PL/SQL

I have say, start and end date as "6/11/1996" and "3/1/2002" in "mm/dd/yyyy" format respectively.
I need to get all the monthly periods as given below.
Start Date End Date
6/11/1996 - 6/30/1996
7/01/1996 - 7/31/1996
8/01/1996 - 8/31/1996
.
.
.
Till
2/01/2002 - 2/28/2002
Any help would be highly appreciated.
Assuming the interval is given by way of two bind variables, :from_dt and :to_dt (strings in the indicated format):
with
inputs ( f_dt, t_dt ) as (
select to_date(:from_dt, 'mm/dd/yyyy'), to_date(:to_dt, 'mm/dd/yyyy') from dual
),
ld ( l_day, lvl ) as (
select add_months(last_day(f_dt), level - 1), level
from inputs
connect by level <= months_between(last_day(t_dt), last_day(f_dt)) + 1
)
select case when ld.lvl = 1 then i.f_dt else add_months(ld.l_day, -1) + 1 end
as start_date,
least(i.t_dt, ld.l_day) as end_date
from inputs i cross join ld
;
This assumes that in the original post you did, in fact, mean to have one more interval, from 3/1/2002 to 3/1/2002; and the query deals correctly with the case when the from-date and the to-date are in the same month: if the inputs are 6/11/1996 to 6/21/1996, then the output is exactly that interval.
Added: creating column aliases in the declaration of factored subqueries (in the WITH clause), as I have done, requires Oracle 11.2 or above. For earlier versions, it is necessary to write it a little differently, like so:
with
inputs as (
select to_date(:from_dt, 'mm/dd/yyyy') as f_dt,
to_date(:to_dt , 'mm/dd/yyyy') as t_dt
from dual
),
ld as (
select add_months(last_day(f_dt), level - 1) as l_day, level as lvl
from inputs ...............
You could use connect by query with functions add_months and months_between:
with p as ( select date '1996-06-11' d1, date '2002-03-01' d2 from dual )
select greatest(trunc(add_months(d1, level - 1), 'month'), d1) as d1,
trunc(add_months(d1, level), 'month') - 1 as d2
from p connect by level <= months_between(trunc(d2, 'month'), trunc(d1, 'month'))
The output is exactly as requested.
DECLARE
L_min_date DATE := '11-Jun-1996';
L_max_date DATE := '28-Feb-2002';
L_number_of_months NUMBER := 0;
L_new_date DATE := NULL;
BEGIN
L_number_of_months := MONTHS_BETWEEN(L_max_date, L_min_date);
DBMS_OUTPUT.PUT_LINE('Start_Date End_Date');
FOR i IN 1..L_number_of_months
LOOP
SELECT (ADD_MONTHS(L_min_date, 1) - 1) INTO L_new_date FROM dual;
DBMS_OUTPUT.PUT_LINE(L_min_date || ' - ' || L_new_date);
L_min_date := L_new_date + 1;
END LOOP;
END;
/
Something like above can help.

recursive cte working very slow

I want to Group the rows based on certain columns, i.e. if data is same in these columns in continuous rows, then assign same Group Number to them, and if its changed, assign new one. This become complex as the same data in the columns could appear later in some other rows, so they have to be given another Group Number as they are not in continuous rows with previous group.
I used cte for this purpose and it is giving correct output also, but is so slow that iterating over 75k+ rows takes about 15 minutes. The code I used is:
WITH
cte AS (SELECT ROW_NUMBER () OVER (ORDER BY Patient_ID, Opnamenummer, SPECIALISMEN, Opnametype, OntslagDatumTijd) AS RowNumber,
Opnamenummer, Patient_ID, AfdelingsCode, Opnamedatum, Opnamedatumtijd, Ontslagdatum, Ontslagdatumtijd, IsSpoedopname, OpnameType, IsNuOpgenomen, SpecialismeCode, Specialismen
FROM t_opnames)
SELECT * INTO #ttt FROM cte;
WITH cte2 AS (SELECT TOP 1 RowNumber,
1 AS GroupNumber,
Opnamenummer, Patient_ID, AfdelingsCode, Opnamedatum, Opnamedatumtijd, Ontslagdatum, Ontslagdatumtijd, IsSpoedopname, OpnameType, IsNuOpgenomen, SpecialismeCode, Specialismen
FROM #ttt
ORDER BY RowNumber
UNION ALL
SELECT c1.RowNumber,
CASE
WHEN c2.Afdelingscode <> c1.Afdelingscode
OR c2.Patient_ID <> c1.Patient_ID
OR c2.Opnametype <> c1.Opnametype
THEN c2.GroupNumber + 1
ELSE c2.GroupNumber
END AS GroupNumber,
c1.Opnamenummer,c1.Patient_ID,c1.AfdelingsCode,c1.Opnamedatum,c1.Opnamedatumtijd,c1.Ontslagdatum,c1.Ontslagdatumtijd,c1.IsSpoedopname,c1.OpnameType,c1.IsNuOpgenomen, SpecialismeCode, Specialismen
FROM cte2 c2
JOIN #ttt c1 ON c1.RowNumber = c2.RowNumber + 1
)
SELECT *
FROM cte2
OPTION (MAXRECURSION 0) ;
DROP TABLE #ttt
I tried to improve performance by putting output of cte in a temp table. That increased the performance, but still its too slow. So, how can I increase the performance of this code to run it under 10 seconds for 75k+ records? The output before cancelling the query is: Screenshot. As visible from the image, data is same in columns Afdelingscode,Patient_ID and Opnametype in RowNumber 3,5 and 6, but they have different GroupNumber because of concurrency of the rows.
Without data its not that easy to test but i would try first to not use temporary table and just use both cte from start to end, ie;
;WITH
cte AS (...),
cte2 AS (...)
select * from cte2
OPTION (MAXRECURSION 0);
Without knowing indices etc... for instance, you do a lot of ordering in the first cte. Is this supported by indices (or one multicolumn index) or not?
Without the data i don't have the option to play with it but looking at this:
CASE
WHEN c2.Afdelingscode <> c1.Afdelingscode
OR c2.Patient_ID <> c1.Patient_ID
OR c2.Opnametype <> c1.Opnametype
THEN c2.GroupNumber + 1
ELSE c2.GroupNumber
i would try to take a look at partition by statement in row_number
So try to run this:
WITH
cte AS (
SELECT ROW_NUMBER () OVER (PARTITION BY Afdelingscode , Patient_ID ,Opnametype ORDER BY Patient_ID, Opnamenummer, SPECIALISMEN, Opnametype, OntslagDatumTijd ) AS RowNumber,
Opnamenummer, Patient_ID, AfdelingsCode, Opnamedatum, Opnamedatumtijd, Ontslagdatum, Ontslagdatumtijd, IsSpoedopname, OpnameType, IsNuOpgenomen
FROM t_opnames)

modifying query resultset to include all dates in a range

I have a query which I run on a table TXN_DEC(id, resourceid, usersid, date, eventdesc) which return distinct count of users for a given date-range and resourceid, group by date and eventdesc (each resource can have 4 to 5 eventdesc)
if there is no value of distinct users count on a date in the range, for an eventdesc, then it skips that date row in the resultset.
I need to have all date rows in my resultset or collection such that if there is no value of count for a date,eventdesc combination, then its value is set to 0 but that date still exists in the collection..
How do I go about getting such a collection
I know getting the final dataset entirely from the query result would be too complicated,
but I can use collections in groovy to modify and populate my map/list to get the data in the required format
something similar to following: if
input date range = 5th Feb to 3 March 2011
DataMap = [dateval: '02/05/2011' eventdesc: 'Read' dist_ucnt: 23,
dateval: '02/06/2011' eventdesc: 'Read' dist_ucnt: 23,
dateval: '02/07/2011' eventdesc: 'Read' dist_ucnt: 0, -> this row was not present in query resultset, but row exists in the map with value 0
....and so on till 3 march 2011 and then whole range repeated for each eventdesc
]
If you want all dates (including those with no entries in your TXN_DEC table) for a given range, you could use Oracle to generate your date range and then use an outer join to your existing query. Then you would just need to fill in null values. Something like:
select
d.dateInRange as dateval,
'Read' as eventdesc,
nvl(td.dist_ucnt, 0) as dist_ucnt
from (
select
to_date('02-FEB-2011','dd-mon-yyyy') + rownum - 1 as dateInRange
from all_objects
where rownum <= to_date('03-MAR-2011','dd-mon-yyyy') - to_date('02-FEB-2011','dd-mon-yyyy') + 1
) d
left join (
select
date,
count(distinct usersid) as dist_ucnt
from
txn_dec
where eventDesc = 'Read'
group by date
) td on td.date = d.dateInRange
That's my purely Oracle solution since I'm not a Groovy guy (well, actually, I am a pretty groovy guy...)
EDIT: Here's the same version wrapped in a stored procedure. It should be easy to call if you know the API....
create or replace procedure getDateRange (
p_begin_date IN DATE,
p_end_date IN DATE,
p_event IN txn_dec.eventDesc%TYPE,
p_recordset OUT SYS_REFCURSOR)
AS
BEGIN
OPEN p_recordset FOR
select
d.dateInRange as dateval,
p_event as eventdesc,
nvl(td.dist_ucnt, 0) as dist_ucnt
from (
select
p_begin_date + rownum - 1 as dateInRange
from all_objects
where rownum <= p_end_date - p_begin_date + 1
) d
left join (
select
date,
count(distinct usersid) as dist_ucnt
from
txn_dec
where eventDesc = p_event
group by date
) td on td.date = d.dateInRange;
END getDateRange;

Resources