Oracle
 sql >> Database >  >> RDS >> Oracle

Passaggio di parametri di input dinamici a 'esegui Immediato'

Non puoi fornire un elenco di stringhe di valori di associazione come using parametro, quindi l'unico modo che posso vedere per farlo è con chiamate SQL dinamiche nidificate, che è un po' disordinato e significa dover dichiarare (e vincolare) tutti i possibili parametri all'interno. istruzione nidificata e dinamica.

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;
/

Ho usato il meccanismo di quotazione alternativo qui per ridurre la confusione da virgolette singole sfuggite. Ci sono due livelli nidificati di virgolette:quello esterno delimitato da q'[...]' e quello interno delimitato da q'^...^' , ma puoi usare altri caratteri se questi rappresentano un problema a causa del contenuto effettivo della tabella. Sfuggire a quelle citazioni per due livelli sarebbe piuttosto brutto e difficile da seguire/dare ragione; e dovresti anche preoccuparti di sfuggire ulteriormente alle virgolette nella tua condition strings, che sarebbe già un problema con il tuo codice esistente per il secondo esempio che hai fornito in quanto contiene un letterale di testo al suo interno.

Con le due righe della tabella di esempio e i valori di data/giorno fittizi che ho mostrato sopra l'output dell'esecuzione che è:

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

La prima cosa da notare nell'istruzione generata è la sezione dichiara, che deve elencare tutti i possibili nomi di variabile che potresti avere in input_params e impostarli da nuove variabili di collegamento. Devi conoscerli già nel blocco/procedura principale, sia come variabili locali che come argomenti di procedure più probabili; ma hanno tutti il ​​duplicato qui, visto che a questo punto non sai quale sarà necessario.

Quindi quell'istruzione ha il suo SQL dinamico interno che è essenzialmente ciò che stavi facendo originariamente, ma si concatena nel input_params stringa e condition .

La parte importante qui è la citazione. Nel primo, ad esempio, entrambi :p_end_date e :p_start_date sono all'interno del secondo livello di virgolette, all'interno di q'^...^' , quindi sono legati all'SQL dinamico interno, con valori dal v_end_date locale e v_start_date da quel execute immediate interno .

L'intero blocco generato viene eseguito con valori di collegamento per tutti i possibili nomi di variabile, che forniscono valori per le variabili locali (tramite v_start_date date := :v_start_date; ecc.) preservando i tipi di dati; più il flag di uscita.

Quel blocco quindi esegue il suo execute immediate interno istruzione utilizzando solo le variabili locali rilevanti, che ora hanno valori vincolati; e il flag di output che è ancora una variabile di collegamento dall'esterno execute immediate , in modo che il blocco esterno possa ancora vedere il suo risultato.

Puoi vedere che la seconda istruzione generata utilizza una condizione diversa e associa variabili e valori alla prima e il flag viene valutato in base alla condizione e ai parametri rilevanti in ogni caso.

Per inciso, potresti rimuovere il riferimento duplicato a :o_flag (che non è un problema ma trovo leggermente confuso) usando invece un'espressione 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;]';