1

I want to perform an update on a huge table on a table like this (I now it's not best practise):

TARGET_TABLE (
TICKET_ID number,
product_id number,
NET number(15,2),
VAT number(15,2));

http://sqlfiddle.com/#!4/d39ed/3

Aim: UPDATE TARGET_TABLE set NET=VAT, VAT=NET

I came up with a BULK UPDATE, but I get an ORA-00913: "To many values" at line 43 which I can't explain. Also, I don't know how to update two rows at once in that variant. Could anyone help out?

DECLARE

-- new data
    CURSOR new_data_cur IS
      select 
                     a.rowid, 
                     a.TICKET_ID,
                     a.product_id,
                     b.NET,
                     b.VAT
 from TARGET_TABLE a
                     join TARGET_TABLE_COPY b
                     on  ( a.TICKET_ID=b.TICKET_ID AND  a.product_id =b.product_id ) ;

    TYPE new_data_type IS TABLE OF new_data_cur%rowtype INDEX BY PLS_INTEGER;
    new_data_tab       new_data_type;
    TYPE row_id_type IS TABLE OF ROWID INDEX BY PLS_INTEGER;
    row_id_tab         row_id_type;
    TYPE rt_update_cols IS RECORD (
        NET   TARGET_TABLE.NET%TYPE
      --  VAT   TARGET_TABLE.VAT%TYPE
    );
    TYPE update_cols_type IS
        TABLE OF rt_update_cols INDEX BY PLS_INTEGER;
    update_cols_tab1    update_cols_type;
    --update_cols_tab2    update_cols_type;
    dml_errors EXCEPTION;
    PRAGMA exception_init ( dml_errors,-24381 );

BEGIN

    OPEN new_data_cur;
    LOOP
        FETCH new_data_cur BULK COLLECT INTO new_data_tab LIMIT 50000;
        EXIT WHEN new_data_tab.count=0;
        FOR i IN new_data_tab.first..new_data_tab.last LOOP
            row_id_tab(i) := new_data_tab(i).rowid;
            update_cols_tab1(i).NET := new_data_tab(i).VAT;
           -- update_cols_tab2(i).VAT := new_data_tab(i).NET;
        END LOOP;

        FORALL i IN new_data_tab.first..new_data_tab.last SAVE EXCEPTIONS # ORA-00913: To many values
            UPDATE TARGET_TABLE
           -- SET row = update_cols_tab(i)
            SET row = update_cols_tab1(i) 
           --         row = update_cols_tab2(i) 
            WHERE ROWID = row_id_tab(i);

        COMMIT;
        EXIT WHEN new_data_tab.count=0;
    END LOOP;
    COMMIT;
    CLOSE new_data_cur;

    EXCEPTION
    WHEN dml_errors THEN

            FOR i IN 1..SQL%bulk_exceptions.count LOOP
            dbms_output.put_line('Some error occured');
            END LOOP;
    END;
9
  • Why are you doing this like this, when you could just do UPDATE TARGET_TABLE set NET=VAT, VAT=NET;? Commented Oct 5, 2018 at 13:15
  • >500.000.000 records Commented Oct 5, 2018 at 13:17
  • So? It's still going to be faster to update the table in a single update statement than to do the bulk update. Commented Oct 5, 2018 at 13:21
  • Alternatively, you could try renaming the columns (net -> net_vat, vat -> net, net_vat -> vat). That assumes that nothing does select * or an insert into target_table without specifying the column list being inserted into in your production code, though. You could potentially reorder the columns using dbms_redefinition if you absolutely needed to, though. Commented Oct 5, 2018 at 13:29
  • 1
    Alternatively you can temporarely add a column UP2DATE INTEGER DEFAULT 0 and then do UPDATE TARGET_TABLE set NET=VAT, VAT=NET, UP2DATE = 1 WHERE UP2DATE = 0 AND ROWNUM < 10000 to update the data chunk wise... Commented Oct 5, 2018 at 13:39

1 Answer 1

1

I believe you don't need an extra cursor where you are swapping the values

FOR i IN new_data_tab.first..new_data_tab.last LOOP
        row_id_tab(i) := new_data_tab(i).rowid;
        update_cols_tab1(i).NET := new_data_tab(i).VAT;
       -- update_cols_tab2(i).VAT := new_data_tab(i).NET;
    END LOOP;

So you code will use these values in your bulk update

DECLARE

-- new data
CURSOR new_data_cur IS
  select 
                 a.rowid, 
                 a.TICKET_ID,
                 a.product_id,
                 b.NET,
                 b.VAT
   from TARGET_TABLE a
                 join TARGET_TABLE_COPY b
                 on  ( a.TICKET_ID=b.TICKET_ID AND  a.product_id =b.product_id ) ;

TYPE new_data_type IS TABLE OF new_data_cur%rowtype INDEX BY PLS_INTEGER;
new_data_tab       new_data_type;
TYPE row_id_type IS TABLE OF ROWID INDEX BY PLS_INTEGER;
row_id_tab         row_id_type;
TYPE rt_update_cols IS RECORD (
    NET   TARGET_TABLE.NET%TYPE
  --  VAT   TARGET_TABLE.VAT%TYPE
);
TYPE update_cols_type IS
    TABLE OF rt_update_cols INDEX BY PLS_INTEGER;
update_cols_tab1    update_cols_type;
--update_cols_tab2    update_cols_type;
dml_errors EXCEPTION;
PRAGMA exception_init ( dml_errors,-24381 );

BEGIN

OPEN new_data_cur;
LOOP
    FETCH new_data_cur BULK COLLECT INTO new_data_tab LIMIT 50000;
    EXIT WHEN new_data_tab.count=0;


    FORALL i IN new_data_tab.first..new_data_tab.last SAVE EXCEPTIONS # ORA-00913: To many values
        UPDATE TARGET_TABLE
       -- SET row = update_cols_tab(i)
       -- SET row = update_cols_tab1(i) 
       --         row = update_cols_tab2(i) 
        NET =  update_cols_tab1(i).VAT
        VAT = update_cols_tab1(i).NET
        WHERE ROWID = row_id_tab(i);

    COMMIT;
    EXIT WHEN new_data_tab.count=0;
END LOOP;
COMMIT;
CLOSE new_data_cur;

EXCEPTION
WHEN dml_errors THEN

        FOR i IN 1..SQL%bulk_exceptions.count LOOP
        dbms_output.put_line('Some error occured');
        END LOOP;
END;
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.