maephisto
maephisto

Reputation: 5182

Oracle optimize splitting rows

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

Answers (1)

Vincent Malgrat
Vincent Malgrat

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

Related Questions