Você não pode fornecer uma lista de strings de valores de ligação como um
using
, então a única maneira que vejo para fazer isso é com chamadas SQL dinâmicas aninhadas, o que é um pouco confuso e significa ter que declarar (e vincular) todos os parâmetros possíveis no arquivo interno. instrução aninhada e dinâmica. 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;
/
Eu usei o mecanismo de cotação alternativo aqui para reduzir a confusão de aspas simples escapadas. Existem dois níveis aninhados de citação - o externo delimitado por
q'[...]'
e o interno delimitado por q'^...^'
, mas você pode usar outros caracteres se esses forem um problema devido ao conteúdo real da tabela. Escapar dessas citações para dois níveis seria muito feio e difícil de seguir/acertar; e você também teria que se preocupar com as aspas de escape em sua condition
strings, o que já seria um problema com seu código existente para o segundo exemplo que você forneceu, pois contém um literal de texto dentro dele. Com suas duas linhas da tabela de amostra e os valores fictícios de data/dia que mostrei acima da saída da execução que é:
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
A primeira coisa a notar na declaração gerada é a seção declare, que deve listar todos os nomes de variáveis possíveis que você pode ter em
input_params
, e defina-os a partir de novas variáveis de ligação. Você deve conhecê-los já no bloco/procedimento principal, seja como variáveis locais ou argumentos de procedimento mais prováveis; mas todos eles devem ser duplicados aqui, pois neste momento você não sabe qual será necessário. Então essa instrução tem seu próprio SQL dinâmico interno que é essencialmente o que você estava fazendo originalmente, mas concatena no
input_params
string, bem como condition
. A parte importante aqui é a citação. No primeiro, por exemplo, tanto
:p_end_date
e :p_start_date
estão dentro do segundo nível de aspas, dentro do q'^...^'
, portanto, eles são vinculados ao SQL dinâmico interno, com valores do local v_end_date
e v_start_date
desse execute immediate
interno . Todo esse bloco gerado é executado com valores de ligação para todos os nomes de variáveis possíveis, que fornecem valores para as variáveis locais (via
v_start_date date := :v_start_date;
etc.) preservando os tipos de dados; mais o sinalizador de saída. Esse bloco então executa seu
execute immediate
interno declaração usando apenas as variáveis locais relevantes, que agora têm valores vinculados; e o sinalizador de saída que ainda é uma variável de ligação do execute immediate
externo , para que o bloco externo ainda possa ver seu resultado. Você pode ver que a segunda instrução gerada usa uma condição diferente e vincula variáveis e valores à primeira, e o sinalizador é avaliado com base na condição e nos parâmetros relevantes em cada caso.
Aliás, você pode remover a referência duplicada para
:o_flag
(o que não é um problema, mas acho um pouco confuso) usando uma expressão case: 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;]';