Sie können keine Zeichenfolgenliste mit Bindungswerten als using
bereitstellen Parameter, also kann ich dies nur mit verschachtelten dynamischen SQL-Aufrufen tun, was ein bisschen chaotisch ist und bedeutet, dass alle möglichen Parameter im Inneren deklariert (und gebunden) werden müssen. verschachtelte, dynamische Anweisung.
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;
/
Ich habe der alternative Zitiermechanismus
hier, um Verwirrung durch einzelne Escape-Anführungszeichen zu vermeiden. Es gibt zwei verschachtelte Zitierebenen - die äußere wird durch q'[...]'
begrenzt und die innere durch q'^...^'
begrenzt , aber Sie können andere Zeichen verwenden, wenn diese aufgrund Ihrer tatsächlichen Tabelleninhalte ein Problem darstellen. Diese Anführungszeichen für zwei Ebenen zu umgehen, wäre ziemlich hässlich und schwer zu befolgen/richtig zu machen; und Sie müssten sich auch um weitere Escape-Anführungszeichen in Ihrer condition
kümmern Zeichenfolgen, was bereits ein Problem mit Ihrem vorhandenen Code für das zweite von Ihnen bereitgestellte Beispiel wäre, da dieser ein Textliteral enthält.
Mit Ihren zwei Beispieltabellenzeilen und den Dummy-Datums-/Tagwerten, die ich oben gezeigt habe, lautet die Ausgabe von running:
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
Das erste, was in der generierten Anweisung zu beachten ist, ist der Deklarationsabschnitt, der alle möglichen Variablennamen auflisten muss, die Sie möglicherweise in input_params
haben , und setzen Sie sie aus neuen Bindungsvariablen. Sie müssen diese bereits im Hauptblock/der Hauptprozedur kennen, entweder als lokale Variablen oder eher als Prozedurargumente; aber sie alle müssen hier dupliziert werden, da Sie zu diesem Zeitpunkt nicht wissen, welche benötigt werden.
Dann hat diese Anweisung ihr eigenes inneres dynamisches SQL, was im Wesentlichen das ist, was Sie ursprünglich getan haben, aber in den input_params
verkettet wird string sowie condition
.
Der wichtige Teil hier ist das Zitieren. Im ersten beispielsweise beide :p_end_date
und :p_start_date
befinden sich innerhalb der zweiten Ebene der Anführungszeichen innerhalb des q'^...^'
, also sind sie für das innere dynamische SQL gebunden, mit Werten aus dem lokalen v_end_date
und v_start_date
von diesem inneren execute immediate
.
Dieser gesamte generierte Block wird mit Bindungswerten für alle möglichen Variablennamen ausgeführt, die Werte für die lokalen Variablen bereitstellen (über v_start_date date := :v_start_date;
usw.) unter Beibehaltung von Datentypen; plus das Ausgabe-Flag.
Dieser Block führt dann sein internes execute immediate
aus Anweisung, die nur die relevanten lokalen Variablen verwendet, die jetzt gebundene Werte haben; und das Ausgabe-Flag, das immer noch eine Bind-Variable aus dem äußeren execute immediate
ist , sodass der äußere Block sein Ergebnis immer noch sehen kann.
Sie können sehen, dass die zweite generierte Anweisung eine andere Bedingung verwendet und Variablen und Werte an die erste bindet, und das Flag in jedem Fall basierend auf der relevanten Bedingung und Parametern ausgewertet wird.
Übrigens könnten Sie den doppelten Verweis auf :o_flag
entfernen (was kein Problem ist, aber ich finde es etwas verwirrend), indem Sie stattdessen einen Fallausdruck verwenden:
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;]';