Nie możesz podać listy ciągów wartości wiązania jako using
parametr, więc jedynym sposobem, w jaki mogę to zrobić, są zagnieżdżone dynamiczne wywołania SQL, co jest nieco niechlujne i oznacza konieczność zadeklarowania (i powiązania) wszystkich możliwych parametrów w wewnętrznym. zagnieżdżona, dynamiczna instrukcja.
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;
/
Użyłem alternatywny mechanizm cytowania
tutaj, aby zmniejszyć zamieszanie związane z pojedynczymi cudzysłowami. Istnieją dwa zagnieżdżone poziomy cytowania - zewnętrzny ograniczony przez q'[...]'
a wewnętrzny oddzielony przez q'^...^'
, ale możesz użyć innych znaków, jeśli stanowią one problem ze względu na rzeczywistą zawartość tabeli. Ucieczka od tych cytatów na dwa poziomy byłaby dość brzydka i trudna do naśladowania/dokładnia; i musiałbyś się również martwić o dalsze unikanie cudzysłowów w swoim condition
ciągi, co już stanowiłoby problem z istniejącym kodem dla drugiej dostarczonej próbki, ponieważ zawiera ona w sobie literał tekstowy.
Z dwoma przykładowymi wierszami tabeli i fikcyjnymi wartościami daty/dni, które pokazałem powyżej danych wyjściowych z uruchomienia, to:
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
Pierwszą rzeczą, na którą należy zwrócić uwagę w wygenerowanej instrukcji, jest sekcja define , która musi zawierać listę wszystkich możliwych nazw zmiennych, które możesz mieć w input_params
i ustaw je z nowych zmiennych wiązania. Musisz je znać już w głównym bloku/procedurze, jako zmienne lokalne lub bardziej prawdopodobne argumenty procedury; ale wszystkie muszą być tutaj zduplikowane, ponieważ w tym momencie nie wiesz, co będzie potrzebne.
Następnie ta instrukcja ma swój własny wewnętrzny dynamiczny SQL, który jest zasadniczo tym, co robiłeś pierwotnie, ale łączy się w input_params
string oraz condition
.
Ważną częścią jest tutaj cytowanie. W pierwszym, na przykład, oba :p_end_date
i :p_start_date
znajdują się na drugim poziomie cudzysłowów, w obrębie q'^...^'
, więc są powiązane z wewnętrznym dynamicznym kodem SQL, z wartościami z lokalnego v_end_date
i v_start_date
z tego wewnętrznego execute immediate
.
Cały wygenerowany blok jest wykonywany z wartościami wiązania dla wszystkich możliwych nazw zmiennych, które dostarczają wartości dla zmiennych lokalnych (poprzez v_start_date date := :v_start_date;
itp.) z zachowaniem typów danych; plus flaga wyjścia.
Ten blok następnie wykonuje swoje wewnętrzne execute immediate
instrukcja używająca tylko odpowiednich zmiennych lokalnych, które mają teraz powiązane wartości; i flaga wyjściowa, która nadal jest zmienną bind z zewnętrznego execute immediate
, aby zewnętrzny blok nadal mógł zobaczyć swój wynik.
Widać, że druga wygenerowana instrukcja używa innego warunku i wiąże zmienne i wartości z pierwszym, a flaga jest oceniana w każdym przypadku na podstawie odpowiedniego warunku i parametrów.
Nawiasem mówiąc, możesz usunąć zduplikowane odniesienie do :o_flag
(co nie stanowi problemu, ale wydaje mi się nieco mylące), używając zamiast tego wyrażenia przypadku:
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;]';