2010-02-18 36 views
8

Ecco il mio cursore:Come posso trovare il numero di record in un cursore Oracle PL/SQL?

CURSOR C1 IS SELECT * FROM MY_TABLE WHERE SALARY < 50000 FOR UPDATE; 

apro subito il cursore per bloccare questi record per la durata della mia procedura.

Desidero generare un errore dell'applicazione nel caso in cui ci siano < 2 record nel mio cursore. L'utilizzo della proprietà ROWCOUNT C1% non riesce perché conta solo il numero che è stato recuperato fino ad ora.

Qual è lo schema migliore per questo caso d'uso? Devo creare una variabile MY_TABLE% ROWTYPE fittizia e quindi scorrere il cursore per recuperarli e mantenere un conteggio, oppure esiste un modo più semplice? Se questo è il modo di farlo, recupererà tutte le righe nel mio cursore chiudendolo implicitamente, sbloccando così quelle righe, o rimarrà aperto finché non lo chiudo esplicitamente anche se le ho recuperate tutte?

Ho bisogno di assicurarmi che il cursore rimanga aperto per una serie di altri compiti oltre questo conteggio.

risposta

6

NB: Ho appena riletto si mette in discussione .. e si vuole fallire se c'è solo 1 record di .. vi posterò un nuovo aggiornamento in un momento ..

Iniziamo qui ..

Da Guida di riferimento Oracle® Database utente PL/SQL di 10g Release 2 (10.2) Numero parte B14261-01 reference

Tutte le righe di un re bloccato quando apri il cursore, non come vengono recuperati.Le righe vengono sbloccate quando si esegue il commit o si esegue il rollback della transazione. Poiché le righe non sono più bloccate, non è possibile recuperare da un cursore FOR UPDATE dopo un commit.

quindi non è necessario preoccuparsi dello sblocco dei record.

in modo da provare questo ..

declare 
    CURSOR mytable_cur IS SELECT * FROM MY_TABLE WHERE SALARY < 50000 FOR UPDATE; 

    TYPE mytable_tt IS TABLE OF mytable_cur %ROWTYPE 
    INDEX BY PLS_INTEGER; 

    l_my_table_recs mytable_tt; 
    l_totalcount NUMBER; 
begin 

    OPEN mytable_cur ; 
    l_totalcount := 0; 

    LOOP 
     FETCH mytable_cur 
     BULK COLLECT INTO l_my_table_recs LIMIT 100; 

     l_totalcount := l_totalcount + NVL(l_my_table_recs.COUNT,0); 

     --this is the check for only 1 row.. 
     EXIT WHEN l_totalcount < 2; 

     FOR indx IN 1 .. l_my_table_recs.COUNT 
     LOOP 
     --process each record.. via l_my_table_recs (indx) 

     END LOOP; 

     EXIT WHEN mytable_cur%NOTFOUND; 
    END LOOP; 

    CLOSE mytable_cur ; 
end; 

ALTERNATIVO RISPOSTA ho letto si risponde a ritroso di avviare e pensato si voleva uscire se non ci fosse più di 1 fila .. non esattamente uno. . Ecco la mia risposta precedente.

2 semplici modi per verificare SOLO 1 record.

Opzione 1 - Fetchs espliciti

declare 
    CURSOR C1 IS SELECT * FROM MY_TABLE WHERE SALARY < 50000 FOR UPDATE; 
    l_my_table_rec C1%rowtype; 
    l_my_table_rec2 C1%rowtype; 
begin 

    open C1; 
    fetch c1 into l_my_table_rec; 

    if c1%NOTFOUND then 
     --no data found 
    end if; 

    fetch c1 into l_my_table_rec2; 
    if c1%FOUND THEN 
     --i have more then 1 row 
    end if; 
    close c1; 

    -- processing logic 

end; 

Mi auguro che si ottiene l'idea.

Opzione 2 - Eccezione Facendo

declare 
    CURSOR C1 IS SELECT * FROM MY_TABLE WHERE SALARY < 50000 FOR UPDATE; 
    l_my_table_rec C1%rowtype; 
begin 
    begin 
    select * 
     from my_table 
     into l_my_table_rec 
    where salary < 50000 
     for update; 
    exception 
    when too_many_rows then 
     -- handle the exception where more than one row is returned 
    when no_data_found then 
     -- handle the exception where no rows are returned 
    when others then raise; 
    end; 

    -- processing logic 
end; 

Inoltre Ricorda: con un cursore esplicito .. si può% tipo la variabile off the record del cursore piuttosto che la tabella originale.

questo è particolarmente utile quando si accede alla query.

Inoltre, rememebr è possibile aggiornare le righe della tabella con un tipo di dichiarazione

UPDATE table_name 
SET set_clause 
WHERE CURRENT OF cursor_name; 

, ma che funziona solo se si dispone di non 'inverosimile' la seconda fila ..


per qualche informazione in più su cursore per i cicli .. provare Here

+0

Grazie per la risposta esauriente. Speravo che ci potesse essere un modo più semplice per ottenere un conteggio dei record nel cursore senza effettivamente recuperare tutti quei dati, ma preferisco questa soluzione per l'introduzione dei punti di salvataggio. È difficile leggere un sacco di record in variabili a cui non ho alcuna utilità (questo è davvero solo un blocco, e ho bisogno di sapere quanti record ho bloccato). Il mio caso d'uso è che ho bisogno di cancellare un record, ma deve esserne sempre uno; dopo aver verificato che 2 esiste nel mio cursore, emetto un'eliminazione separata per individuare il record specifico che volevo rimuovere. –

+0

okay .. forse dovresti provare il contrario .. e solo su un file DELETE dritto sulle righe 'duplicate' .. se non esistono .. allora non succede nulla. se lo fanno ti rimane 1? .. es. DELETE FROM my_table WHERE (seleziona solo le righe duplicate qui); – ShoeLace

0

Creare un punto di salvataggio prima di scorrere il cursore e quindi utilizzare un rollback parziale quando ci sono < 2 record restituiti.

0

È possibile avviare la transazione e verificare se SELECT COUNT (*) MY_TABLE WHERE STIPENDIO < 50000 maggiore di 1.

1

Se stai cercando di non riuscire whenver avete più di 1 riga restituita, provate questo:

declare 
    l_my_table_rec my_table%rowtype; 
begin 
    begin 
    select * 
     from my_table 
     into l_my_table_rec 
    where salary < 50000 
     for update; 
    exception 
    when too_many_rows then 
     -- handle the exception where more than one row is returned 
    when no_data_found then 
     -- handle the exception where no rows are returned 
    when others then raise; 
    end; 

    -- processing logic 
end; 
1

Se questo è il modo per farlo, sarà recupero tutte le righe nel mio cursore implicitamente chiuderlo, sbloccando in tal modo le righe

I blocchi saranno presenti per la durata della transazione (ovvero fino a quando non si esegue un commit o rollback) indipendentemente da quando (o se) si chiude il cursore.

mi piacerebbe andare per

declare 
    CURSOR C1 IS SELECT * FROM MY_TABLE WHERE SALARY < 50000 FOR UPDATE;; 
    v_1 c1%rowtype; 
    v_cnt number; 
begin 
    open c_1; 
    select count(*) into v_cnt FROM MY_TABLE WHERE SALARY < 50000 and rownum < 3; 
    if v_cnt < 2 then 
    raise_application_error(-20001,'...'); 
    end if; 
    --other processing 
    close c_1; 
end; 

C'è una probabilità molto piccola che, tra il momento il cursore viene aperto (le righe di chiusura) e il select count, qualcuno inserisce una o più righe nella tabella con un stipendio inferiore a 50000. In tal caso l'errore dell'applicazione verrebbe aumentato ma il cursore elaborerebbe solo le righe presenti quando il cursore è stato aperto. Se questo è un problema, alla fine esegui un altro controllo su un conteggio delle righe pari a $ 1 e, se il problema si è verificato, avresti bisogno di eseguire il rollback su un punto di salvataggio.

Problemi correlati