Reputation: 135
I have two tables table_Day and table_A
table_Day
I | Date |
---|---|
1 | 05-02-2021 |
2 | 05-03-2021 |
3 | 05-04-2021 |
4 | 05-05-2021 |
5 | 05-06-2021 |
and so on. It goes till 365 days
Table_B
Cust | Added_Date | Bill No | Cust_Setup_Dt |
---|---|---|---|
Mark | 05-03-2021 | 1A | 05-03-2021 |
Mark | 05-03-2021 | 1P | 05-03-2021 |
Dave | 05-03-2021 | 35B | 05-03-2021 |
Joan | 05-03-2021 | 40C | 05-03-2021 |
Mark | 05-04-2021 | 2A | 05-03-2021 |
Mark | 05-04-2021 | 54A | 05-03-2021 |
Mark | 05-04-2021 | 88A | 05-03-2021 |
Phil | 05-04-2021 | 3Z | 05-04-2021 |
Joan | 05-04-2021 | 2D | 05-03-2021 |
Joan | 05-04-2021 | 57L | 05-03-2021 |
Mark | 05-05-2021 | 10E | 05-03-2021 |
STUK | 05-05-2021 | 12F | 05-05-2021 |
WILL | 05-05-2021 | 20ER | 05-05-2021 |
and so on. I need to find the cumulative count of bills for each customer for each day, upto that day. The sample output would look something like this
Cust | Day | Cumulative Count of Bills |
---|---|---|
Mark | 05-03-2021 | 2 |
Mark | 05-04-2021 | 5 |
Mark | 05-05-2021 | 6 |
Joan | 05-03-2021 | 1 |
Joan | 05-04-2021 | 3 |
Here, on day 1 Mark had only 2 bills. On day 2, Mark had 3 bills, so total cumulative count becomes Count of (Day1 + Day2) = 5. On day 3 total count becomes (count(day 1 + day 2 + day 3)) = 6 and so on. Need to do this for all customers. Current code which I have is logically correct, but because of the size of data (table_A has billions of data points across hundreds of rows and columns) and table_Day is for 365 days. Computationally it is extremely time consuming.
with table_Day (I, DT) AS
(
SELECT 1, SYSDATE FROM DUAL
UNION ALL
SELECT I + 1, DT - INTERVAL '1' DAY FROM Dates_Index WHERE I < 365
)
select A.cust,A.count(distinct (A.Bill_No)) as cul_count,A.Added_Date,A.cust_setup_dt,D.DT
from table_Day D
cross join lateral
table_A A
WHERE a.ADDED_DATE between (A.Cust_setup_dt) and (D.DT)
group by A.cust,D.DT,A.Added_Date
This code takes too much time that it is not feasible computationally. Since the cumulative count is basically just count of all bills till previous day + current day, iterating through the table for all Added_Date for every Day in table_Day seems doing same processes again and again. Instead I was thinking to store the cumulative count in a temporary variable and update it by adding the count of the current day to the number already stored. I want the logic to be something like this.
for cust = x (where x = 1 to n)
(Declare @temp_cul_count = 0
Declare @temp_added_date
(for i = 05-02-2021 to (05-06-2021)
cul_count = count(distinct(bills)
temp_cul_count = temp_cul_count + cul_count
print(cul_count),print(table_day.DAY) /*Basically storing the updated cul_count and day in table*/
i++
if i > a.added_date
break)
)
x++
The above code is basic logic which I want to implement. However, I have no idea how to put it in Oracle SQL.
Upvotes: 0
Views: 81
Reputation: 167867
You can use a PARTITION
ed OUTER JOIN
to join your calendar for each customer and then find the cumulative total with the SUM
analytic function:
WITH calendar (day) AS (
SELECT TRUNC(SYSDATE, 'YYYY') + LEVEL - 1
FROM DUAL
CONNECT BY TRUNC(SYSDATE, 'YYYY') + LEVEL - 1 < ADD_MONTHS(TRUNC(SYSDATE, 'YYYY'), 12)
)
select A.cust,
C.day,
SUM(COALESCE(bill_count, 0)) OVER (PARTITION BY a.cust ORDER BY C.day)
AS cum_bill_count
from calendar c
LEFT OUTER JOIN (
SELECT cust,
added_date,
COUNT(DISTINCT bill_no) AS bill_count
FROM table_a
GROUP BY
cust,
added_date
) a
PARTITION BY (a.cust)
ON (c.day = a.added_date)
db<>fiddle here
Upvotes: 1