sql >> Base de Datos >  >> RDS >> Oracle

Pasar parámetros de entrada dinámicos para 'ejecutar inmediatamente'

No puede proporcionar una lista de cadenas de valores de vinculación como using parámetro, por lo que la única forma que veo para hacer esto es con llamadas SQL dinámicas anidadas, lo cual es un poco complicado y significa tener que declarar (y vincular) todos los parámetros posibles en el interior. declaración dinámica anidada.

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

He usado el mecanismo de cotización alternativo aquí para reducir la confusión de las comillas simples escapadas. Hay dos niveles anidados de comillas:el exterior delimitado por q'[...]' y el interior delimitado por q'^...^' , pero puede usar otros caracteres si esos son un problema debido al contenido real de su tabla. Escapar de esas comillas para dos niveles sería bastante feo y difícil de seguir/acertar; y también tendría que preocuparse por las comillas de escape adicionales en su condition cadenas, lo que ya sería un problema con su código existente para la segunda muestra que proporcionó, ya que contiene un texto literal dentro.

Con sus dos filas de tabla de muestra y los valores ficticios de fecha/día que mostré arriba, el resultado de la ejecución es:

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

Lo primero que debe tener en cuenta en la declaración generada es la sección de declaración, que tiene que enumerar todos los nombres de variables posibles que pueda tener en input_params y configúrelos a partir de nuevas variables de vinculación. Debe conocerlos ya en el bloque/procedimiento principal, ya sea como variables locales o como argumentos de procedimiento más probables; pero todos tienen que ser duplicados aquí, ya que en este punto no sabes cuales serán necesarios.

Luego, esa declaración tiene su propio SQL dinámico interno, que es esencialmente lo que estaba haciendo originalmente, pero se concatena en input_params cadena así como condition .

La parte importante aquí es la cita. En el primero, por ejemplo, tanto :p_end_date y :p_start_date están dentro del segundo nivel de comillas, dentro del q'^...^' , por lo que están vinculados al SQL dinámico interno, con valores del v_end_date local y v_start_date desde ese interior execute immediate .

Todo el bloque generado se ejecuta con valores de vinculación para todos los nombres de variables posibles, que proporcionan valores para las variables locales (a través de v_start_date date := :v_start_date; etc.) conservando los tipos de datos; más la bandera de salida.

Ese bloque luego ejecuta su execute immediate interno declaración que usa solo las variables locales relevantes, que ahora tienen valores vinculados; y el indicador de salida que sigue siendo una variable de vinculación desde el exterior execute immediate , por lo que el bloque exterior todavía puede ver su resultado.

Puede ver que la segunda declaración generada usa una condición diferente y vincula variables y valores a la primera, y la bandera se evalúa en función de la condición y los parámetros relevantes en cada caso.

Por cierto, podría eliminar la referencia duplicada a :o_flag (lo cual no es un problema pero me parece un poco confuso) usando una expresión de mayúsculas y minúsculas en su lugar:

    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;]';