Avatar billede lbaad Nybegynder
23. juni 2004 - 11:21 Der er 10 kommentarer og
1 løsning

Hvorfor tager det så lang tid(procedure)

Hejsa
Nu har jeg et problem med en procedure. Når jeg eksekverer følgende:
BEGIN
delete_commit('delete account_specification where create_date <= to_date(''06-02-2004:23:59:59'',''DD-MM-YYYY:HH24:MI:SS'')',10000);
END;
så tager det meget meget lang tid. Sletter 10000 rækker ad gangen. Der er ca 1000000 rækker pr dag, og når jeg kører statementet, så skal jeg bruge 1 dag, for at slette 1 dag.
Proceduren kommer her:

( p_statement in varchar2,
  p_commit_batch_size  in number default 10000)
is
        cid                          integer;
        changed_statement              varchar2(2000);
        finished                        boolean;
        nofrows                    integer;
        lrowid                          rowid;
    rowcnt                          integer;
        errpsn    integer;
        sqlfcd                          integer;
        errc                      integer;
        errm                            varchar2(2000);
begin
        /* If the actual statement contains a WHERE clause, then append a
          rownum < n clause after that using AND, else use WHERE
    rownum < n clause */
        if ( upper(p_statement) like '% WHERE %') then
                changed_statement := p_statement||' AND rownum < '
              ||to_char(p_commit_batch_size + 1);
        else
changed_statement := p_statement||' WHERE rownum < '
||to_char(p_commit_batch_size + 1);
        end if;
        begin
  cid := dbms_sql.open_cursor; -- Open a cursor for the task
                dbms_sql.parse(cid,changed_statement, dbms_sql.native);
                        -- parse the cursor. Pleae note that in Oracle 7.2.2
                        -- parsing does a execute too. But that does not
                        -- pose a problem here as we want that.
                rowcnt := dbms_sql.last_row_count;
                      -- store for some future reporting
        exception
          when others then
                    errpsn := dbms_sql.last_error_position;
                        -- gives the error position in the changed sql
                        -- delete statement if anything happens
    sqlfcd := dbms_sql.last_sql_function_code;
                        -- function code can be found in the OCI manual
                    lrowid := dbms_sql.last_row_id;
                        -- store all these values for error reporting. However
                        -- all these are really useful in a stand-alone proc
                        -- execution for dbms_output to be successful, not
                        -- possible when called from a form or front-end tool.
                    errc := SQLCODE;
                    errm := SQLERRM;
                    dbms_output.put_line('Error '||to_char(errc)||
                          ' Posn '||to_char(errpsn)||
              ' SQL fCode '||to_char(sqlfcd)||
        ' rowid '||rowidtochar(lrowid));
                    raise_application_error(-20000,errm);
                        -- this will ensure the display of atleast the error
                        -- message if someething happens, even in a frontend
                        -- tool.
        end;
        finished := FALSE;
        while not (finished)
        loop -- keep on executing the cursor till there is no more to process.
                begin
nofrows := dbms_sql.execute(cid);
                        rowcnt := dbms_sql.last_row_count;
                exception
                        when others then
                                errpsn := dbms_sql.last_error_position;
                        sqlfcd := dbms_sql.last_sql_function_code;
                    lrowid := dbms_sql.last_row_id;
    errc := SQLCODE;
                                errm := SQLERRM;
                      dbms_output.put_line('Error '||to_char(errc)||
                            ' Posn '||to_char(errpsn)||
                ' SQL fCode '||to_char(sqlfcd)||
          ' rowid '||rowidtochar(lrowid));
                                raise_application_error(-20000,errm);
                end;
                if nofrows = 0 then
                        finished := TRUE;
                else
                finished := FALSE;
                end if;
                commit;
        end loop;
        begin
                dbms_sql.close_cursor(cid);
                        -- close the cursor for a clean finish
        exception
                when others then
                        errpsn := dbms_sql.last_error_position;
                        sqlfcd := dbms_sql.last_sql_function_code;
                        lrowid := dbms_sql.last_row_id;
    errc := SQLCODE;
                        errm := SQLERRM;
      dbms_output.put_line('Error '||to_char(errc)||
      ' Posn '||to_char(errpsn)||
                                ' SQL fCode '||to_char(sqlfcd)||
                                ' rowid '||rowidtochar(lrowid));
                        raise_application_error(-20000,errm);
        end;
end;

Kan i se der er noget, som ikke helt er optimeret??
Mvh
Lars
Avatar billede teepee Nybegynder
23. juni 2004 - 13:13 #1
Er der index på create_date?
Avatar billede lbaad Nybegynder
23. juni 2004 - 13:36 #2
Ja, det er der.
Avatar billede Slettet bruger
23. juni 2004 - 21:21 #3
er create_date defineret som not null-felt? Ellers skal der jo stadig laves et full table-scan hver gang.

har du husket at lave en analyze på din tabel? Det kunne jo være at din explain plan er helt i skoven.

Hvor lang tid tager det at lave en select count(*) over de samme data?
Avatar billede lbaad Nybegynder
24. juni 2004 - 09:07 #4
Det er tilladt, at indsætte NULL's i create_date. Der ligger lige nu 272 rækker, med NULL i create_date.
Jeg har ikke lavet en analyze på tabellen, det laver jeg snarest.
Kan jeg først se min explain plan, efter jeg har lavet en analyze table??
Det tager over 15 minutter, at lave en select count(*) på hele tabellen. Gad ikke lige vente på den blev færdig, så jeg stoppede efter 15 min.
Avatar billede lbaad Nybegynder
24. juni 2004 - 09:17 #5
Vil en analyze table ikke tage en masse performance ud af basen??
Det er et produktionsmiljø, så det går jo ikke, at jeg ligger hele basen ned!!!
Avatar billede lbaad Nybegynder
05. juli 2004 - 10:49 #6
det har intet hjulpet, at sætte create_date til NOT NULL. Det tager stadig det meste af en dag, at slette ca 1 mill rækker.
Avatar billede Slettet bruger
05. juli 2004 - 19:34 #7
Hvad mener du med at du afbryder din select count(*)?
Det er jo først i det øjeblik at du laver en select på databasen at de committede transaktioner 'skrives' ned i databasen. Det er derfor at selve operationen commit er så hurtig.

En analyze behøver ikke at tage meget performance. Du kan nøjes med at lave en
'sample' hvor du nøjes med en brøkdel af tabellen, det er ofte nok.


Kan du selv finde dokumentationen? Det er noget med

ANALYZE TABLE account_specification ESTIMATE STATISTICS;

Denne statement tager de første 1064 rækker som udgangspunkt.


Held og lykke med det.
Avatar billede lbaad Nybegynder
13. juli 2004 - 08:36 #8
Hvad får jeg helt præcist ud af, at lave en analyze på tabellen??
Jeg har nu fundet ud af, at hvis jeg bruger proceduren fra en SQL*plus, istedet for at køre den fra TOAD, så kører det relativt hurtigt.
Det skal jo også tage lidt tid, at slette ca. 1 mill rækker.
Takker for svaret, lig et svar, og så får du points.
Avatar billede lbaad Nybegynder
22. juli 2004 - 09:48 #9
Det viser sig nu også, at det kører meget hurtigere på Oracle 9i, og da vi planlægger at opgradere til 9i, så bliver der ikke problemer for fremtiden.
Avatar billede olet Nybegynder
08. oktober 2004 - 12:13 #10
Jeg kan anbefale at du kigger nærmere på PL/SQL's "forall" funktion, så kan du slette fx 1000 rækker af gangen. PL/SQL bruger i din version meget tid på context switching mellem PL/SQL "laget" og selve "SQL-laget". Der vil helt sikkert være noget at hente her, også selv om det kører fornuftigt nu.

Eksempel:

begin

    declare

        type row_table is table of rowid index by binary_integer;
        array_size number := 1000;
        my_row_array row_table;

        cursor csr_deleteinstances is
                select rowid
                  from <table>
                  where col = 'something';
      begin

        open csr_deleteinstances;
        loop

            fetch csr_deleteinstances bulk collect into my_row_array limit array_size;
            exit when my_row_array.count = 0;

            forall j in 1..my_row_array.last
              delete from <table> where rowid = my_row_array(j);
            commit;

            exit when csr_deleteinstances%NOTFOUND;

        end loop;

        close csr_deleteinstances;
        my_row_array.delete;

    end ;

end;
/
Avatar billede lbaad Nybegynder
08. oktober 2004 - 14:02 #11
Det er som sagt ikke et problem mere, da vi er gået over på 9i, og jeg slette på normal vis nu. Takker for alle indlæg
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Computerworld tilbyder specialiserede kurser i database-management

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester