Reputation: 5182
I have two tables Quantity(user, year, quantity) and MonthlyQuantity(user, year, month, quantity) - the monthly one is empty. What i need to do is to fill the monthly table based on the yearly one, by dividing the yearly qty in 12 equal parts, and adding any rest to the last month. So basically, each row in Quantity should create 12 rows in QuantityMonthly. Here's how i do it - the problem is that the loop is very slow.
How could i do it faster?
create ro replace
procedure pr_test
AS
BEGIN
FOR r IN (SELECT * FROM Quantity) LOOP
DELETE FROM QuantityMonthly qm WHERE qm.company = r.company AND qm.year = r.year;
INSERT ALL
INTO QuantityMonthly (company, year, mon, qty) values (r.company, r.year, 1, trunc(r.quantity / 12))
INTO QuantityMonthly (company, year, mon, qty) values (r.company, r.year, 2, trunc(r.quantity / 12))
INTO QuantityMonthly (company, year, mon, qty) values (r.company, r.year, 3, trunc(r.quantity / 12))
INTO QuantityMonthly (company, year, mon, qty) values (r.company, r.year, 4, trunc(r.quantity / 12))
INTO QuantityMonthly (company, year, mon, qty) values (r.company, r.year, 5, trunc(r.quantity / 12))
INTO QuantityMonthly (company, year, mon, qty) values (r.company, r.year, 6, trunc(r.quantity / 12))
INTO QuantityMonthly (company, year, mon, qty) values (r.company, r.year, 7, trunc(r.quantity / 12))
INTO QuantityMonthly (company, year, mon, qty) values (r.company, r.year, 8, trunc(r.quantity / 12))
INTO QuantityMonthly (company, year, mon, qty) values (r.company, r.year, 9, trunc(r.quantity / 12))
INTO QuantityMonthly (company, year, mon, qty) values (r.company, r.year, 10, trunc(r.quantity / 12))
INTO QuantityMonthly (company, year, mon, qty) values (r.company, r.year, 11, trunc(r.quantity / 12))
INTO QuantityMonthly (company, year, mon, qty) values (r.company, r.year, 12, trunc(r.quantity / 12) + mod(r.quantity , 12))
SELECT * FROM dual;
END LOOP;
COMMIT;
END pr_test;
Upvotes: 0
Views: 63
Reputation: 67722
Use a single statement, a single set operation is often much faster than many small operations:
MERGE INTO QuantityMonthly qm
USING (SELECT *
FROM Quantity q
CROSS JOIN (SELECT rownum mon
FROM dual
CONNECT BY level <= 12)) q
ON (qm.company = q.company
AND qm.year = q.year
AND qm.mon = q.mon)
WHEN MATCHED THEN
UPDATE SET qm.quantity = q.quantity / 12
+ CASE WHEN q.mon = 12 THEN
mod(r.quantity, 12)
ELSE
0
END
WHEN NOT MATCHED THEN
INSERT (company, year, mon, qty)
VALUES (q.company, q.year, q.mon,
q.quantity / 12
+ CASE WHEN q.mon = 12 THEN mod(r.quantity, 12) ELSE 0 END);
Upvotes: 1