sqlplus - usando una variabile di bind nella clausola "in"
-
12-11-2019 - |
Domanda
Sto impostando una variabile di bind in un blocco PL/SQL e sto cercando di usarlo in un'altra query in espressione. Qualcosa come questo:
variable x varchar2(255)
declare
x varchar2(100);
begin
for r in (select id from other_table where abc in ('&val1','&val2','&val3') ) loop
x := x||''''||r.id||''',';
end loop;
--get rid of the trailing ','
x:= substr(x,1,length(x)-1);
select x into :bind_var from dual;
end;
/
print :bind_var;
select *
from some_table
where id in (:bind_var);
E ricevo un errore (ORA-01722: numero non valido) sulla query che cerca di utilizzare la variabile BIND nell'elenco "in".
L'istruzione di stampa restituisce '123','345'
che è quello che mi aspetto.
È possibile utilizzare la variabile di bind come questa o dovrei provare un approccio diverso?
(usando Oracle 10G)
Una precisazione:
Questo è per una specie di riconciliazione. voglio correre
select *
from some_table
where id in (select id from other_table where abc in ('&val1','&val2','&val3'))
Prima della parte principale della sceneggiatura (non nella foto qui) elimina un sacco di record. Voglio eseguirlo di nuovo in seguito per verificare che i record in some_table
non sono stati eliminati. Tuttavia, i dati in other_table
Viene eliminato da questo processo, quindi non posso semplicemente fare riferimento ai dati in other_table
Perché non c'è niente lì. Ho bisogno di un modo per preservare il other_table.id
Valori in modo da poter verificare i record dei genitori in seguito.
Soluzione
Conserverei il other_table.id
In una tabella PL/SQL e riferimento in quella tabella nella query in seguito:
type t_id_table is table OF other_table.id%type index by binary_integer;
v_table t_id_table;
-- fill the table
select id
bulk collect into v_table
from other_table
where abc in ('&val1','&val2','&val3');
-- then at a later stage...
select *
from some_table st
, table(cast(v_table AS t_id_table)) idt
where st.id = idt.id;
Altri suggerimenti
Non è possibile utilizzare valori separati da virgola in una variabile di bind.
Potresti dire:
select * from some_table where id in (:bind_var1, :bind_var2)
anche se
Stai meglio usando qualcosa di simile:
select * from some_table where id in ("select blah blah blah...");
Userei una tabella temporanea globale per questo scopo
create global temporary table gtt_ids( id number ) ;
poi
...
for r in (select id from other_table where ... ) loop
insert into gtt_ids(id) values (r.id) ;
end loop;
...
E alla fine
select *
from some_table
where id in (select id from gtt_ids);
OK, ho una specie di brutta soluzione che utilizza anche variabili di sostituzione ...
col idList NEW_VALUE v_id_list /* This is NEW! */
variable x varchar2(255)
declare
x varchar2(100);
begin
for r in (select id from other_table where abc in ('&val1','&val2','&val3') ) loop
x := x||''''||r.id||''',';
end loop;
--get rid of the trailing ','
x:= substr(x,1,length(x)-1);
select x into :bind_var from dual;
end;
/
print :bind_var;
select :x idList from dual; /* This is NEW! */
select *
from some_table
where id in (&idList); /* This is CHANGED! */
Funziona, ma accetterò una risposta da qualcun altro se è più elegante.
ha cambiato il ciclo da utilizzare Listagg (Purtroppo questo funzionerà solo in 11gr2).
Ma per la variabile nell'elenco, ho usato a espressione regolare Per raggiungere l'obiettivo (ma pre 10g puoi usare substr per fare lo stesso) questo viene sollevato dalla domanda asktom collegata.
variable bind_var varchar2(255)
variable dataSeperationChar varchar2(255)
declare
x varchar2(100);
begin
select listagg(id,',') within group(order by id) idList
into x
from(select level id
from dual
connect by level < 100 )
where id in (&val1,&val2,&val3) ;
select x into :bind_var from dual;
:dataSeperationChar := ',';
end;
/
print :bind_var;
/
select *
from (
select level id2
from dual
connect by level < 100
)
where id2 in(
select -- transform the comma seperated string into a result set
regexp_substr(:dataSeperationChar||:bind_var||','
, '[^'||:dataSeperationChar||']+'
,1
,level) as parsed_value
from dual
connect by level <= length(regexp_replace(:bind_var, '([^'||:dataSeperationChar||'])', '')) + 1
)
;
/*
values of 1,5, and 25
BIND_VAR
------
1,5,25
ID2
----------------------
1
5
25
*/
MODIFICARE
Oops ha appena notato che hai segnato 10G, l'unica cosa da fare è non usare il Listagg che ho fatto all'inizio