U kunt geen stringlijst met bindwaarden als een using
parameter, dus de enige manier die ik kan zien om dit te doen, is met geneste dynamische SQL-aanroepen, wat een beetje rommelig is, en betekent dat ik alle mogelijke parameters in de binnenste moet declareren (en binden). geneste, dynamische verklaring.
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;
/
Ik heb het alternatieve citatiemechanisme
hier om verwarring door ontsnapte enkele aanhalingstekens te verminderen. Er zijn twee geneste niveaus van aanhalingstekens - de buitenste begrensd door q'[...]'
en de binnenste begrensd door q'^...^'
, maar u kunt andere tekens gebruiken als dat een probleem is vanwege uw werkelijke tabelinhoud. Ontsnappen aan die aanhalingstekens voor twee niveaus zou behoorlijk lelijk zijn en moeilijk te volgen / goed te krijgen; en je zou je ook zorgen moeten maken over het ontsnappen van aanhalingstekens in je condition
strings, wat al een probleem zou zijn met je bestaande code voor het tweede voorbeeld dat je hebt opgegeven, omdat dat een letterlijke tekst bevat.
Met je twee voorbeeldtabelrijen en de dummy-datum / dag-waarden die ik boven de uitvoer van hardlopen liet zien, namelijk:
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
Het eerste dat opvalt in de gegenereerde instructie is de declare-sectie, die alle mogelijke variabelenamen moet vermelden die je zou kunnen hebben in input_params
en stel ze in vanuit nieuwe bindvariabelen. U moet deze al in het hoofdblok/de hoofdprocedure kennen, hetzij als lokale variabelen, hetzij als meer waarschijnlijke procedure-argumenten; maar ze moeten hier allemaal worden gedupliceerd, omdat je op dit moment niet weet welke je nodig hebt.
Dan heeft die instructie zijn eigen innerlijke dynamische SQL, wat in wezen is wat u oorspronkelijk deed, maar samenvoegt in de input_params
tekenreeks en condition
.
Het belangrijkste hier is het citeren. In de eerste bijvoorbeeld, zowel :p_end_date
en :p_start_date
bevinden zich binnen het tweede niveau van aanhalingstekens, binnen de q'^...^'
, dus ze zijn gebonden aan de innerlijke dynamische SQL, met waarden van de lokale v_end_date
en v_start_date
vanuit die innerlijke execute immediate
.
Dat hele gegenereerde blok wordt uitgevoerd met bindwaarden voor alle mogelijke variabelenamen, die waarden leveren voor de lokale variabelen (via v_start_date date := :v_start_date;
enz.) met behoud van gegevenstypen; plus de uitvoervlag.
Dat blok voert dan zijn interne execute immediate
statement met alleen de relevante lokale variabelen, die nu gebonden waarden hebben; en de uitvoervlag die nog steeds een bindvariabele is van de buitenste execute immediate
, zodat het buitenste blok het resultaat nog steeds kan zien.
U kunt zien dat de tweede gegenereerde instructie een andere voorwaarde gebruikt en variabelen en waarden aan de eerste bindt, en de vlag wordt in elk geval geëvalueerd op basis van de relevante voorwaarde en parameters.
Overigens zou je de dubbele verwijzing naar :o_flag
. kunnen verwijderen (wat geen probleem is, maar ik vind het een beetje verwarrend) door in plaats daarvan een hoofdletteruitdrukking te gebruiken:
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;]';