Du kan ikke levere en strengliste over bindeværdier som en using
parameter, så den eneste måde jeg kan se at gøre dette på er med indlejrede dynamiske SQL-kald, som er lidt rodet og betyder, at man skal deklarere (og binde) alle mulige parametre i den indre. indlejret, dynamisk udsagn.
declare
v_execute_statement varchar2(4000);
v_flag varchar2(1);
v_start_date date := date '2018-01-01';
v_end_date date := date '2018-01-31';
v_joining_day varchar2(9) := 'MONDAY';
begin
-- loop over all rows for demo
for rec in (
select condition, input_params
From your_table
)
loop
v_execute_statement := q'[
DECLARE
v_start_date date := :v_start_date;
v_end_date date := :v_end_date;
v_joining_day varchar2(9) := :v_joining_day;
BEGIN
EXECUTE IMMEDIATE q'^
BEGIN
IF ]' || rec.condition || q'[ THEN
:o_flag := 'Y';
ELSE
:o_flag := 'N';
END IF;
END;^'
USING ]' || rec.input_params || q'[, OUT :v_flag;
END;]';
dbms_output.put_line('Statement: ' || v_execute_statement);
EXECUTE IMMEDIATE v_execute_statement
USING v_start_date, v_end_date, v_joining_day, OUT v_flag;
dbms_output.put_line('Result flag: ' || v_flag);
end loop;
end;
/
Jeg har brugt den alternative tilbudsmekanisme
her for at reducere forvirring fra undslupne enkelt citater. Der er to indlejrede niveauer af citering - det ydre afgrænset af q'[...]'
og den indre afgrænset af q'^...^'
, men du kan bruge andre tegn, hvis de er et problem på grund af dit faktiske tabelindhold. At undslippe disse citater for to niveauer ville være ret grimt og svært at følge/få rigtigt; og du skulle også bekymre dig om yderligere undslippende citater i din condition
strenge, som allerede ville være et problem med din eksisterende kode for den anden prøve, du har angivet, da den indeholder en bogstavelig tekst i den.
Med dine to eksempeltabelrækker og dummy-dato/dag-værdierne viste jeg over outputtet fra kørsel, dvs.:
Statement:
DECLARE
v_start_date date := :v_start_date;
v_end_date date := :v_end_date;
v_joining_day varchar2(9) := :v_joining_day;
BEGIN
EXECUTE IMMEDIATE q'^
BEGIN
IF :p_end_date < :p_start_date THEN
:o_flag := 'Y';
ELSE
:o_flag := 'N';
END IF;
END;^'
USING v_end_date, IN v_start_date, OUT :o_flag;
END;
Result flag: N
Statement:
DECLARE
v_start_date date := :v_start_date;
v_end_date date := :v_end_date;
v_joining_day varchar2(9) := :v_joining_day;
BEGIN
EXECUTE IMMEDIATE q'^
BEGIN
IF :p_joining_day = 'MONDAY' THEN
:o_flag := 'Y';
ELSE
:o_flag := 'N';
END IF;
END;^'
USING v_joining_day, OUT :o_flag;
END;
Result flag: Y
Den første ting at bemærke i den genererede sætning er deklareringssektionen, som skal liste alle de mulige variabelnavne, du måtte have i input_params
, og sæt dem fra nye bindevariabler. Du skal kende disse allerede i hovedblokken/proceduren, enten som lokale variabler eller mere sandsynlige procedureargumenter; men de skal alle kopieres her, da du på nuværende tidspunkt ikke ved, hvad der skal til.
Så har den sætning sin egen indre dynamiske SQL, som i det væsentlige er det, du oprindeligt lavede, men kædes sammen i input_params
streng samt condition
.
Den vigtige del her er citatet. I den første, for eksempel, begge :p_end_date
og :p_start_date
er inden for det andet niveau af anførselstegn, inden for q'^...^'
, så de er bundet til den indre dynamiske SQL, med værdier fra den lokale v_end_date
og v_start_date
fra den indre execute immediate
.
Hele den genererede blok udføres med bindeværdier for alle mulige variabelnavne, som giver værdier for de lokale variabler (via v_start_date date := :v_start_date;
osv.), mens datatyper bevares; plus outputflaget.
Denne blok udfører derefter sin interne execute immediate
sætning, der kun bruger de relevante lokale variabler, som nu har bundne værdier; og outputflaget, som stadig er en bindingsvariabel fra den ydre execute immediate
, så den ydre blok stadig kan se sit resultat.
Du kan se, at den anden genererede sætning bruger en anden betingelse og binder variabler og værdier til den første, og flaget evalueres baseret på den relevante betingelse og parametre i hvert enkelt tilfælde.
I øvrigt kunne du fjerne den duplikerede reference til :o_flag
(hvilket ikke er et problem, men jeg synes lidt forvirrende) ved at bruge et kasusudtryk i stedet:
v_execute_statement := q'[
DECLARE
v_start_date date := :v_start_date;
v_end_date date := :v_end_date;
v_joining_day varchar2(9) := :v_joining_day;
BEGIN
EXECUTE IMMEDIATE q'^
BEGIN
:o_flag := CASE WHEN ]' || rec.condition || q'[ THEN 'Y' ELSE 'N' END;
END;^'
USING OUT :v_flag, ]' || rec.input_params || q'[;
END;]';