2009-06-03 5 views
7

(Oracle PL/SQL)Come determinare l'errore di lancio di righe/valori nell'istruzione PL/SQL?

Se ho un'istruzione SQL semplice che sta gettando un errore, vale a dire:

DECLARE 
    v_sql_errm varchar2(2048); 
BEGIN 
    UPDATE my_table SET my_column = do_something(my_column) 
     WHERE my_column IS NOT NULL; 
EXCEPTION 
    when others then 
     -- How can I obtain the row/value causing the error (unknown)? 
     v_sql_errm := SQLERRM; 
     insert into log_error (msg) values ('Error updating value (unknown): '|| 
      v_sql_errm); 
END; 

C'è un modo all'interno del blocco eccezione per determinare la riga/valore su cui la query ha riscontrato un errore? Mi piacerebbe essere in grado di registrarlo in modo che possa quindi entrare e modificare/correggere il valore specifico dei dati che causano l'errore.

+1

In base alle risposte di seguito, non sembra essere un modo per determinare l'effettiva riga/valore che causa l'errore. La soluzione migliore sembra essere l'iterazione utilizzando un ciclo FOR. –

+0

Domanda correlata: http://stackoverflow.com/questions/18458012/oracle-jdbc-how-to-know-which-row-throws-unique-key-constraint – Vadzim

risposta

2

Una soluzione che utilizza le eccezioni clausola di SAVE:

SQL> create table my_table (my_column) 
    2 as 
    3 select level from dual connect by level <= 9 
    4/

Table created. 

SQL> create function do_something 
    2 (p_my_column in my_table.my_column%type 
    3 ) return my_table.my_column%type 
    4 is 
    5 begin 
    6 return 10 + p_my_column; 
    7 end; 
    8/

Function created. 

SQL> alter table my_table add check (my_column not in (12,14)) 
    2/

Table altered. 

SQL> declare 
    2 e_forall_error exception; 
    3 pragma exception_init(e_forall_error,-24381) 
    4 ; 
    5 type t_my_columns is table of my_table.my_column%type; 
    6 a_my_columns t_my_columns := t_my_columns() 
    7 ; 
    8 begin 
    9 select my_column 
10   bulk collect into a_my_columns 
11  from my_table 
12 ; 
13 forall i in 1..a_my_columns.count save exceptions 
14  update my_table 
15   set my_column = do_something(a_my_columns(i)) 
16  where my_column = a_my_columns(i) 
17 ; 
18 exception 
19 when e_forall_error then 
20 for i in 1..sql%bulk_exceptions.count 
21 loop 
22  dbms_output.put_line(a_my_columns(sql%bulk_exceptions(i).error_index)); 
23 end loop; 
24 end; 
25/
2 
4 

PL/SQL procedure successfully completed. 

Per set di dati molto grandi, probabilmente non si vuole far esplodere la memoria PGA, quindi assicurarsi di utilizzare la clausola LIMIT in tal caso.

0

prova a inviare il tuo errore e vedere se ti dà le informazioni che stai cercando. Per esempio:

EXCEPTION 
    WHEN OTHERS 
    THEN 
     DBMS_OUTPUT.PUT_LINE(SQLERRM); 
END; 
+0

Grazie ma mi dà solo una descrizione dell'errore come "mese non valido" per le operazioni di data o "valore troppo grande" se il valore supera la dimensione della colonna. Ho bisogno di sapere quale valore ha causato quell'errore in modo da poter apportare le correzioni appropriate ai dati. –

0

Per maggiori dettagli informazioni riguardo alle modalità di esecuzione arrivato alla linea in questione, si potrebbe provare a visualizzare l'output restituito da queste funzioni:

  • DBMS_UTILITY.format_error_stack:

    Formatta lo stack di errori corrente. Questo può essere utilizzato nei gestori di eccezioni per esaminare l'intero stack di errori.

  • DBMS_UTILITY.format_error_backtrace:

    Formato il backtrace dal punto dell'errore corrente al gestore di eccezioni in cui è stato catturato l'errore. La stringa NULL viene restituita se non viene attualmente gestito alcun errore.

0

Prova questo (non testato):

DECLARE 
     cursor c1 is 
     select key_column, my_column 
     from my_table 
     WHERE my_column IS NOT NULL 
     ORDER BY key_column; 

     my_table_rec my_table%ROWTYPE; 

    BEGIN 

     FOR my_table_rec in c1 
     LOOP 
     UPDATE my_table SET my_column = do_something(my_column) 
      WHERE key_column = my_table_rec.key_column; 
     END LOOP; 

    EXCEPTION 
     when others then 
     insert into log_error (msg) values ('Error updating key_column: ' || my_table_rec.key_column || ', my_column: ' || my_table_rec.my_column); 
    END; 
+0

-1 per suggerire di passare da una singola istruzione di aggiornamento a una elaborazione riga per riga (anche nota come lenta per lento). –

+0

OK Rob! Devi avere in mente che "non puoi mangiare la tua torta e averla anche tu". La mia soluzione funziona. E i tuoi? – Christian

+0

Sì, ho pensato di iterarli singolarmente e ottenerlo in questo modo ... è un set di dati ENORME e speravo di ottenere la risposta direttamente senza dover iterare (e imparare qualcosa di nuovo allo stesso tempo!). È comunque un buon consiglio, grazie! –

0

PL/SQL definisce 2 variabili globali per fare riferimento a errori:

SQLERRM: errore di SQL Messaggio

SQLERRNO: Errore SQL Numero

Questo è leggibile nell'EXCE Blocco PTION in PL/SQL.

+0

Questo mi dà l'errore, non il valore che ha generato l'errore. –

3

Questo può essere fatto utilizzando la registrazione degli errori DML, se si è su 10gR2 o successivo.

Un esempio:

SQL> create table my_table (my_column) 
    2 as 
    3 select level from dual connect by level <= 9 
    4/

Tabel is aangemaakt. 

SQL> create function do_something 
    2 (p_my_column in my_table.my_column%type 
    3 ) return my_table.my_column%type 
    4 is 
    5 begin 
    6 return 10 + p_my_column; 
    7 end; 
    8/

Functie is aangemaakt. 

SQL> alter table my_table add check (my_column not in (12,14)) 
    2/

Tabel is gewijzigd. 

SQL> exec dbms_errlog.create_error_log('my_table') 

PL/SQL-procedure is geslaagd. 

Questo crea una tabella di registrazione degli errori chiamato sbagliare $ _my_table.Questa tabella è riempita con l'aggiunta di una clausola di errori nel registro per la sua dichiarazione di aggiornamento:

SQL> begin 
    2 update my_table 
    3  set my_column = do_something(my_column) 
    4  where my_column is not null 
    5   log errors reject limit unlimited 
    6 ; 
    7 end; 
    8/

PL/SQL-procedure is geslaagd. 

SQL> select * from err$_my_table 
    2/

         ORA_ERR_NUMBER$ 
-------------------------------------- 
ORA_ERR_MESG$ 
-------------------------------------------------------------------- 
ORA_ERR_ROWID$ 
-------------------------------------------------------------------- 
OR 
-- 
ORA_ERR_TAG$ 
-------------------------------------------------------------------- 
MY_COLUMN 
-------------------------------------------------------------------- 
            2290 
ORA-02290: check constraint (RWK.SYS_C00110133) violated 
AAGY/aAAQAABevcAAB 
U 

12 

            2290 
ORA-02290: check constraint (RWK.SYS_C00110133) violated 
AAGY/aAAQAABevcAAD 
U 

14 


2 rijen zijn geselecteerd. 

Prima 10gR2, è possibile utilizzare le eccezioni clausola di SAVE: http://rwijk.blogspot.com/2007/11/save-exceptions.html

+0

Ho letto tutto questo, oltre al link del tuo blog e, a meno di non fraintendermi, mi dà ancora solo i messaggi di errore (ad esempio SQLCODE e SQLERRM) e non il valore dei dati che causa l'errore. Questo purtroppo non è utile. A proposito, sono bloccato con l'uso di 9g. –

+0

Se guardi da vicino il contenuto di err $ _my_table, noterai my_column che dice 12 e 14, che sono i nuovi valori che non sono riusciti ad aggiornare. Pubblicherò un'altra risposta, in particolare per la tua versione 9i. Anche perché mi fa male agli occhi vedere che hai scelto un approccio fila per fila :-) –