2009-05-22 5 views
7

ho aa ciclo cursore che sta costruendo una stringa concatenando i contenuti di una tabella insieme, utilizzando il codice lungo queste linee:cursore Oracle che attraversa l'ultimo elemento due volte

OPEN cur_t; 
LOOP 
    FETCH cur_t INTO v_texttoadd; 

    v_string := v_string || v_texttoadd; 
EXIT WHEN cur_t%notfound; 
END LOOP; 

Il problema è, naturalmente, che l'ultimo oggetto viene aggiunto due volte perché il sistema lo esegue ancora una volta prima di rendersi conto che non c'è altro da trovare.

Ho provato a giocare con qualcosa di simile

OPEN cur_t; 
WHILE cur_t%found; 
LOOP 
    FETCH cur_t INTO v_texttoadd; 

    v_string := v_string || v_texttoadd; 
END LOOP; 

Ma questo non sembrava di tornare nulla.

Che tipo di sintassi dovrei utilizzare in modo che ogni riga venga visualizzata una sola volta nella stringa risultante?

risposta

21

Si può provare questo:

OPEN cur_t; 
LOOP 
    FETCH cur_t INTO v_texttoadd; 
    EXIT WHEN cur_t%notfound; 
    v_string := v_string || v_texttoadd; 
END LOOP; 

Questo funziona perché% notfound viene impostato quando FETCH viene eseguito e non ce ne sono più righe per andare a prendere. Nel tuo esempio hai controllato% non trovato dopo la concatenazione e di conseguenza hai avuto il duplicato alla fine.

+0

Non ha funzionato per il mio. Dai un'occhiata alla mia risposta se ti interessa. –

0

Risposta semplice, anche se forse non il migliore:

OPEN cur_t; 
LOOP 
    FETCH cur_t INTO v_texttoadd; 
    IF cur_t%found THEN 
     v_string := v_string || v_texttoadd; 
    END IF; 
EXIT WHEN cur_t%notfound; 
END LOOP; 
+0

Penso che, in base al tuo indenting, stai assumendo che l'istruzione EXIT sia parte della sintassi LOOP e debba arrivare alla fine del ciclo. Questo non è vero; EXIT è solo un'affermazione come qualsiasi altra nel corpo del loop. Questo è il motivo per cui, come altri hanno dimostrato, puoi metterlo subito dopo FETCH per risolvere il tuo problema. –

+0

Non ha funzionato per il mio. Dai un'occhiata alla mia risposta se ti interessa. –

2

% notfound viene impostato quando prendere non riesce a recuperare una nuova riga.

un altro modo possibile (questo per evitare il "se" s e "uscita quando" s):

OPEN cur_t; 
FETCH cur_t INTO v_texttoadd; 
WHILE cur_t%found LOOP 
    v_string := v_string || v_texttoadd; 
    FETCH cur_t INTO v_texttoadd; 
END LOOP; 
+0

Ho paura che questa sia la * sola * risposta corretta qui! –

3

giuste risposte sono già state date, ma solo elaborando un po '.

Simulando la vostra situazione attuale:

SQL> declare 
    2 cursor cur_t 
    3 is 
    4 select ename 
    5  from emp 
    6  where deptno = 10 
    7 ; 
    8 v_texttoadd emp.ename%type; 
    9 v_string varchar2(100); 
10 begin 
11 open cur_t; 
12 loop 
13  fetch cur_t into v_texttoadd; 
14  v_string := v_string || v_texttoadd; 
15  exit when cur_t%notfound; 
16 end loop 
17 ; 
18 dbms_output.put_line(v_string); 
19 end; 
20/
CLARKKINGMILLERMILLER 

PL/SQL-procedure is geslaagd. 

Qui MILLER viene stampato due volte. Con solo il passaggio della dichiarazione EXIT e l'assegnazione v_string, si ottiene il risultato desiderato:

SQL> declare 
    2 cursor cur_t 
    3 is 
    4 select ename 
    5  from emp 
    6  where deptno = 10 
    7 ; 
    8 v_texttoadd emp.ename%type; 
    9 v_string varchar2(100); 
10 begin 
11 open cur_t; 
12 loop 
13  fetch cur_t into v_texttoadd; 
14  exit when cur_t%notfound; 
15  v_string := v_string || v_texttoadd; 
16 end loop 
17 ; 
18 dbms_output.put_line(v_string); 
19 end; 
20/
CLARKKINGMILLER 

PL/SQL-procedure is geslaagd. 

Tuttavia, il codice PL/SQL diventa più facile quando si utilizza un cursore-for-loop. È quindi possibile ignorare la variabile v_texttoadd e il numero di linee in loop diminuisce:

SQL> declare 
    2 cursor cur_t 
    3 is 
    4 select ename 
    5  from emp 
    6  where deptno = 10 
    7 ; 
    8 v_string varchar2(100); 
    9 begin 
10 for r in cur_t 
11 loop 
12  v_string := v_string || r.ename; 
13 end loop 
14 ; 
15 dbms_output.put_line(v_string); 
16 end; 
17/
CLARKKINGMILLER 

PL/SQL-procedure is geslaagd. 

È inoltre possibile utilizzare SQL dritto per svolgere il lavoro. Un esempio con la clausola modello di SQL, se si è in versione 10g o superiore:

SQL> select string 
    2 from (select string 
    3    , rn 
    4    from emp 
    5   where deptno = 10 
    6   model 
    7     dimension by (rownum rn) 
    8     measures (ename, cast(null as varchar2(100)) string) 
    9     (string[any] order by rn desc = ename[cv()] || string[cv()+1] 
10     ) 
11  ) 
12 where rn = 1 
13/

STRING 
----------------------------------------------------------------------------------- 
CLARKKINGMILLER 

1 rij is geselecteerd. 

Saluti, Rob.

+0

+1 per il suggerimento FOR ... IN ... LOOP ed esempi. Cursor FOR loops sono i tuoi amici ;-) Bonus aggiuntivo: puoi spesso incorporare la tua istruzione di selezione del cursore nel ciclo FOR: FOR REC IN (SELECT ...) LOOP – DCookie

0

Ho realizzato molte soluzioni basate su cursore in Microsoft SQL, che ha sempre funzionato perfettamente (bei vecchi tempi! Voglio tanto indietro il mio SQL Server!) Tuttavia tutte le risposte a questa domanda si sono rivelate sbagliate per me, proprio l'ultima riga del cursore viene SEMPRE eseguita due volte, indipendentemente da quanto ho seguito il metodo suggerito da loro.

Dimentica il non-tempo loop e dimentica exit, questo è quello che DEVI fare per evitare la doppia esecuzione (più o meno quello che fai anche in T-SQL!).

cursor c_mm is select a, b, c, d from mytable; 

    begin 
    open c_mm; 
    fetch c_mm into a, b, c, d; 
    while (not c_mm%notfound) loop 
     -- do what you have to do here 

     fetch c_mm into a, b, c, d; 
    end loop; 
    close c_mm; 
    end; 

che davvero non capisco è il motivo per tutti gli articoli di Oracle Knowledge Base e post del forum (e StackOverflow) promuove questa soluzione exit-in-a-loop che è ovviamente sbagliato!