2010-02-09 2 views
10

è possibile eseguire una parte dinamica di sql in plsql e restituire i risultati in un sys_refcursor? Ho incollato il mio tentativo soo, ma cucitura dosnt di lavorare, questo è il im errore di ottenere throught mio java appEsecuzione di un'istruzione SQL dinamica in un SYS_REFCURSOR

ORA-01006: variabile di legatura non esiste ORA-06512: alla "LIVEFIS. ERC_REPORT_PK", linea 116 ORA-06512: alla linea 1

ma che potrebbe essere frainteso somthing da Java, tutto cuciture per compilare bene soo im non sicuro.

procedure all_carers_param_dy (pPostcode in carer.postcode%type, pAge Number 
           ,pReport out SYS_REFCURSOR) is 
    begin 
    declare 
     lsql varchar2(500) :='SELECT c.id FROM carer c, cared_for cf,carer_cared_for ccf ' 
      ||' where c.id = ccf.carer_id (+)' 
      ||' AND cf.id (+) = ccf.cared_for_id'; 

    begin 

    if pPostcode is not null and pAge <= 0 then 
     lsql := lsql||' AND c.postcode like ''%''|| upper(pPostcode)||''%'''; 
     elsif pPostcode is null and pAge > 0 then 
     lsql := lsql||' AND ROUND((MONTHS_BETWEEN(sysdate,c.date_of_birth)/12)) = pAge'; 
     elsif pPostcode is not null and pAge > 0 then 
     lsql := lsql ||' AND ROUND((MONTHS_BETWEEN(sysdate,c.date_of_birth)/12)) = pAge' 
         ||' AND c.postcode like ''%''|| upper(pPostcode)||''%'''; 
     end if; 


     execute immediate lsql 
     into pReport; 


    end; 
    end; 

Im nuovo a plsql e ancora più recente per SQL dinamico soo qualsiasi aiuto/suggerimento sarebbe molto apprezzata.

Grazie ancora

Jon

risposta

3

non è possibile assegnare un refcursor attraverso l'uso di esecuzione immediata.

È necessario creare l'SQL in una stringa e quindi utilizzare aperto.

sql_str := 'SELECT * FROM...'; 
open pReport for sql_str; 
+0

Tecnicamente, se il SELECT restituisce un tipo di dati del cursore è possibile selezionare in un cursore ref (che esegue immediato o uno SQL statica). Ma sei corretto nel fatto che questa selezione sta restituendo un valore di identificazione, non un cursore di riferimento. –

7

si dovrà associare i parametri pAge e pPostcode. In SQL dinamico, li si prefiggeva con i due punti (:). Se si utilizza EXECUTE IMMEDIATE o OPEN ... FOR, si legherà i parametri tramite la posizione, questo è il motivo per cui li ho rinominato: P1 e P2 nell'esempio:

DECLARE 
    lsql VARCHAR2(500) := 'SELECT c.id 
          FROM carer c, cared_for cf, carer_cared_for ccf 
          WHERE c.id = ccf.carer_id (+) 
          AND cf.id (+) = ccf.cared_for_id'; 
BEGIN 
    IF pPostcode IS NULL THEN 
     lsql := lsql || ' AND :P1 IS NULL'; 
    ELSE 
     lsql := lsql || ' AND c.postcode like ''%''|| upper(:P1)||''%'''; 
    IF pPostcode pAge > 0 THEN 
     lsql := lsql || ' AND :P2 = ROUND((MONTHS_BETWEEN(sysdate, 
                 c.date_of_birth)/12))'; 
    ELSE 
     lsql := lsql || ' AND nvl(:P2, -1) <= 0'; 
    END IF; 
    OPEN pReport FOR lsql USING pPostcode, pAge; 
END; 

Nota: Il numero e la posizione delle variabili bind deve essere noto al momento della compilazione, questo è il motivo per cui utilizzo spesso il costrutto sopra (aggiungendo il parametro alla sua posizione anche se non è usato). L'aggiunta di una tautologia (come in AND :P1 IS NULL) a una query non influirà sul suo piano di spiegazione.

+1

+1: le variabili CONTESTUALI sono più accomodanti in questa situazione –

+0

@OMG Pony: assolutamente –

+0

+1: Sicuramente. Non so cosa stavo pensando ... –

0

Utilizzare la sintassi OPEN FOR e le variabili di binding.

procedure all_carers_param_dy (pPostcode in carer.postcode%type, pAge Number 
          ,pReport out SYS_REFCURSOR) 
is 
    lsql varchar2(500) :='SELECT c.id FROM carer c, cared_for cf,carer_cared_for ccf ' 
     ||' where c.id = ccf.carer_id (+)' 
     ||' AND cf.id (+) = ccf.cared_for_id'; 

begin 

if pPostcode is not null and pAge <= 0 then 
    lsql := lsql||' AND c.postcode like upper(''%''||:1||''%'')'; 
    open pReport for lsql using pPostcode; 
    elsif pPostcode is null and pAge > 0 then 
    lsql := lsql||' AND ROUND((MONTHS_BETWEEN(sysdate,c.date_of_birth)/12)) = :1'; 
    open pReport for lsql using pAge; 
    elsif pPostcode is not null and pAge > 0 then 
    lsql := lsql ||' AND ROUND((MONTHS_BETWEEN(sysdate,c.date_of_birth)/12)) = :1' 
        ||' AND c.postcode like upper(''%''||:2||''%'')'; 
    open pReport for lsql using pAge, pPostcode; 
    end if; 

end all_carers_param_dy; 
/

SQL dinamico è difficile, difficile da capire e difficile da correggere. Una delle aree difficili è la gestione della ripetizione. È una buona idea dichiarare ripetute sezioni di bolierplate come costanti. Inoltre, tieni presente che possiamo dividere stringhe di grandi dimensioni su più righe senza doverle concatenare con '||'. Ciò riduce il sovraccarico di manutenzione.

create or replace procedure all_carers_param_dy 
    (pPostcode in carer.postcode%type 
     , pAge Number 
     , pReport out SYS_REFCURSOR) 
is 
    lsql varchar2(500) ; 

    root_string constant varchar2(500) :='SELECT c.id FROM carer c 
           , cared_for cf,carer_cared_for ccf 
         where c.id = ccf.carer_id (+) 
         and cf.id (+) = ccf.cared_for_id'; 
    pc_string constant varchar2(256) := 
     ' AND c.postcode like upper(''%''||:pc||''%'')'; 
    age_string constant varchar2(256) := 
     ' AND ROUND((MONTHS_BETWEEN(sysdate,c.date_of_birth)/12)) = :age'; 
begin 

if pPostcode is not null and pAge <= 0 then 
    lsql := root_string || pc_string; 
    open pReport for lsql using pPostcode; 

    elsif pPostcode is null and pAge > 0 then 
    lsql := root_string || age_string; 
    open pReport for lsql using pAge; 

    elsif pPostcode is not null and pAge > 0 then 
    lsql := root_string || age_string 
         || pc_string; 
    open pReport for lsql using pAge, pPostcode; 

    end if; 
end all_carers_param_dy; 
/