2012-03-22 14 views
12

Diciamo che ho uno procedure chiamato myproc. Questo è un processo complesso e non posso permettere che due istanze eseguano contemporaneamente il proc.Esiste un modo migliore per evitare l'esecuzione del processo più di una volta in Oracle?

In realtà io farlo usando il dbms_application_info.set_module:

procedure start_process is 
begin 
    dbms_application_info.set_module('myproc', 'running'); 
end; 

e verificare prima di eseguire il processo:

select 'S' 
    from v$session v 
where v.module = 'myproc' 
    and v.action = 'running'; 

Nel livello di database, c'è un modo migliore per controllare questo?

+1

+1 buona domanda, ma 'v $ session' non è univoco sul modulo, azione quindi sarebbe facile sbagliare. – Ben

+0

Vedere anche http://stackoverflow.com/questions/1053484/block-procedure-pl-sql-with-oracle – gavenkoa

risposta

9

Usa dbms_lock.allocate_unique con dbms_lock.request. Le Note sull'utilizzo dice:

La prima sessione di chiamare ALLOCATE_UNIQUE con un nuovo nome blocco provoca un ID blocco unico per essere generato e memorizzato nella tabella dbms_lock_allocated. Le chiamate successive (di solito di altre sessioni) restituiscono l'ID di blocco generato in precedenza.

Penso che questo potrebbe essere quello che stai cercando.

+0

+1, questa è un'idea molto migliore della mia ...Sono troppo abituato a raccogliere un sacco di metadati contemporaneamente a fare altre cose. – Ben

+0

Caratteristica davvero interessante. Grazie! –

1

È possibile creare una tabella processes. Inoltre, assicurati che ogni processo abbia una sorta di identificatore univoco, ad esempio un hash dello owner, object_name da dba_objects in modo da poterlo creare dinamicamente nel pacchetto.

Quindi si crea una funzione su lock each row individually durante l'esecuzione di un processo.

Come ha sottolineato @Sergio nei commenti, ciò non funzionerebbe se per qualche motivo fosse necessario eseguire il commit nel bel mezzo del processo, a meno che, naturalmente, non venga selezionato nuovamente dopo ogni commit.

function locking (Pid) return number is 

    l_locked number := 0; 

begin 

    select 1 
    into l_locked 
    from processes 
    where id = Pid 
     -- exit immediately if the proc is running 
     for update nowait 
      ; 

    return l_locked; 

    exception when others then 
     return 0; 

end; 

Questo ha il vantaggio di bloccare quella riga nel processes per voi fino a quando la sessione che è attualmente in esecuzione la vostra procedura è terminata.

È quindi avvolgere questo nella procedura:

-- if we failed to lock locking will have thrown an error 
-- i.e. we have 0 here. 
if locking(123) = 0 then 
    exit; 
end if; 

Finché ogni procedura ha un ID univoco - il bit importante - la vostra procedura uscirà in modo pulito.


Potrebbe non si applica nella vostra situazione, ma, il mio modo normale di fare questo è quello di utilizzare mod. Sebbene non si fermi 2 dello stesso processo in esecuzione, garantisce che, quando ne hai più di 1, li esegui solo su dati diversi. Qualcosa di simile a quanto segue:

procedure my_procedure (PNumerator number, PDenominator number) is 

    cursor c_my_cursor (CNumerator number, CDenominator number) is 
    select columns 
     from my_table 
     where mod(ascii(substr(id, -1)), CDenominator) = CNumerator 
      ; 

begin 
    open c_my_cursor(PNumerator, PDenominator); 
    ...  
end; 
+0

Questo si è rotto se per qualche motivo è necessario eseguire il commit nel bel mezzo del processo, giusto? –

+0

@ SérgioMichels, buon punto! Lo aggiungerò alla risposta. – Ben

Problemi correlati