2013-05-17 22 views
5

Quando si tenta di utilizzare una dichiarazione BULK COLLECT, ho ricevuto l'errore ORA-00947: not enough values.BULK COLLECT in una tabella di oggetti

Uno script di esempio:

CREATE OR REPLACE 
TYPE company_t AS OBJECT ( 
    Company   VARCHAR2(30), 
    ClientCnt   INTEGER ); 
/

CREATE OR REPLACE 
TYPE company_set AS TABLE OF company_t;  
/

CREATE OR REPLACE 
FUNCTION piped_set (
    v_DateBegin IN DATE, 
    v_DateEnd IN DATE 
) 
return NUMBER /*company_set pipelined*/ as 
    v_buf company_t := company_t(NULL, NULL); 
    atReport company_set; 
    sql_stmt VARCHAR2(500) := ''; 
begin 

select * BULK COLLECT INTO atReport 
from (
    SELECT 'Descr1', 1 from dual 
    UNION 
    SELECT 'Descr2', 2 from dual) ; 

    return 1; 
end; 

L'errore si verifica in corrispondenza della linea select * BULK COLLECT INTO atReport.

Straight PL/SQL funziona perfettamente (quindi non è necessario menzionarlo come soluzione). L'utilizzo di BULK COLLECT in un tipo di tabella utente è la domanda.

risposta

9

tuo company_set è una tabella di oggetti, e si sta selezionando i valori, non oggetti costituiti da quei valori. Questo compilerà:

select * BULK COLLECT INTO atReport 
from (
    SELECT company_t('Descr1', 1) from dual 
    UNION 
    SELECT company_t('Descr2', 2) from dual) ; 

... ma quando run getterà ORA-22950: cannot ORDER objects without MAP or ORDER method perché il union fa ordinamento implicita per identificare e rimuovere i duplicati, in modo da utilizzare al posto union all:

select * BULK COLLECT INTO atReport 
from (
    SELECT company_t('Descr1', 1) from dual 
    UNION ALL 
    SELECT company_t('Descr2', 2) from dual) ; 
+0

Grazie, @alex, codice davvero aiutato! Come continuazione del problema, è possibile passare il risultato dell'inserimento di massa come risultato della funzione pipeline? Attualmente, ottengo il risultato in atReport e poi eseguo "FOR .. LOOP (pipe_row (atReportRow)) END LOOP;". Forse anche questa parte può essere semplificata? – xacinay

+1

@xacinay - non con 'bulk collect'; si potrebbe invece farlo come un ciclo di cursore ('for rec in (selezionare company_t() come comp ...) loop pipe row rec.comp; end loop;' o simile. Immagino che le prestazioni siano simili. fai una nuova domanda se la provi e non puoi farlo funzionare –

+0

Questo è esattamente il modo in cui funziona: 'per x in (seleziona ..) riga di pipe (company_t (x.Company, x.ClientCnt)))'. Mi sono solo chiesto, può essere ancora più semplice. Ad ogni modo, il risultato attuale è abbastanza buono, grazie! – xacinay