Oracle Tutorial de SQL dinámico de PL/SQL: Ejecución inmediata y DBMS_SQL

⚡ Resumen inteligente

SQL dinámico en Oracle PL/SQL construye y ejecuta sentencias en tiempo de ejecución, adaptando las consultas a los requisitos cambiantes mediante dos enfoques: SQL dinámico nativo con EXECUTE IMMEDIATE y OPEN-FOR, y el paquete flexible DBMS_SQL para casos complejos.

  • ⚙️ SQL en tiempo de ejecución: El SQL dinámico genera y ejecuta sentencias incluso cuando se desconocen de antemano los nombres de las tablas o columnas.
  • SQL dinámico nativo: EXECUTE IMMEDIATE crea y ejecuta SQL rápidamente con el mínimo código.
  • 🔁 ABIERTO PARA: Gestiona consultas dinámicas de varias filas que EXECUTE IMMEDIATE no puede recuperar por sí solo.
  • 🧩 DBMS_SQL: Se ajusta a las sentencias cuyo número de columnas o tipos se desconocen hasta el momento de la ejecución.
  • 🔐 Variables de enlace: La cláusula USING pasa los valores posicionalmente y bloquea la inyección SQL.
  • 🤖 Asistencia de IA: Las herramientas de IA elaboran código SQL dinámico y señalan los riesgos de inyección durante la revisión.

Oracle Tutorial de SQL dinámico PL/SQL

¿Qué es SQL dinámico?

Dynamic SQL SQL es una metodología de programación para generar y ejecutar sentencias en tiempo de ejecución. Se utiliza principalmente para escribir programas flexibles y de propósito general, donde las sentencias SQL se crean y ejecutan en tiempo de ejecución según los requisitos; por ejemplo, cuando los nombres de las tablas, las listas de columnas o las condiciones WHERE no se conocen hasta que se ejecuta el programa.

Formas de escribir SQL dinámico

PL/SQL ofrece dos maneras de escribir SQL dinámico:

  1. NDS – SQL dinámico nativo (las instrucciones EXECUTE IMMEDIATE y OPEN-FOR)
  2. DBMS_SQL (un paquete suministrado)

La regla general es sencilla: si se conocen el número y los tipos de datos de las variables de entrada y salida en tiempo de compilación, utilice SQL dinámico nativo, ya que es más rápido y requiere menos código. Cuando esta información solo se conoce en tiempo de ejecución, utilice el paquete DBMS_SQL.

NDS (SQL dinámico nativo): ejecución inmediata

El SQL dinámico nativo es la forma más sencilla de escribir SQL dinámico. Utiliza el comando EXECUTE IMMEDIATE para crear y ejecutar el SQL en tiempo de ejecución. Para usar este método, es necesario conocer de antemano el tipo de datos y el número de variables que se usarán en tiempo de ejecución. Además, ofrece un mejor rendimiento y menor complejidad en comparación con DBMS_SQL.

Sintaxis

EXECUTE IMMEDIATE dynamic_sql_string
[INTO {variable[, variable]... | record}]
[USING [IN | OUT | IN OUT] bind_argument[, ...]]
[RETURNING INTO bind_argument[, ...]];
  • cadena SQL dinámica: Una expresión de cadena (VARCHAR2 o CHAR, no NVARCHAR2/NCHAR) que contiene una única instrucción SQL o un bloque PL/SQL.
  • Cláusula INTO: Opcional. Se utiliza únicamente cuando la consulta SQL dinámica es una consulta SELECT de una sola fila; captura los valores devueltos en variables o en un registro. Cada columna seleccionada necesita una variable compatible con el tipo de dato.
  • USO de la cláusula: Opcional. Proporciona variables de enlace. El modo predeterminado es ENTRADA; SALIDA y ENTRADA/SALIDA se utilizan para recibir valores.
  • REGRESO A LA cláusula: Se utiliza con sentencias DML que incluyen una cláusula RETURNING para capturar los valores de las filas afectadas en los argumentos de enlace.

Ejemplo 1: En este ejemplo, obtenemos los datos de la tabla emp para el emp_no '1001' utilizando una instrucción NDS con una variable de enlace.

NDS - Ejecutar Inmediato

DECLARE
   lv_sql       VARCHAR2(500);
   lv_emp_name  VARCHAR2(50);
   ln_emp_no    NUMBER;
   ln_salary    NUMBER;
   ln_manager   NUMBER;
BEGIN
   lv_sql := 'SELECT emp_name, emp_no, salary, manager FROM emp WHERE emp_no = :empno';
   EXECUTE IMMEDIATE lv_sql
      INTO lv_emp_name, ln_emp_no, ln_salary, ln_manager
      USING 1001;
   DBMS_OUTPUT.PUT_LINE('Employee Name: '   || lv_emp_name);
   DBMS_OUTPUT.PUT_LINE('Employee Number: ' || ln_emp_no);
   DBMS_OUTPUT.PUT_LINE('Salary: '          || ln_salary);
   DBMS_OUTPUT.PUT_LINE('Manager ID: '      || ln_manager);
END;
/

Resultado

Employee Name  : XXX
Employee Number: 1001
Salary         : 15000
Manager ID     : 1000

Code Explicación:

  • Líneas 2-6: Declarando las variables.
  • Línea 8: Estructuración del SQL en tiempo de ejecución. El SQL contiene la variable de enlace ':empno' en la condición WHERE.
  • Líneas 9-11: Ejecutando la consulta SQL enmarcada con EXECUTE IMMEDIATE. Las variables de la cláusula INTO contienen los valores obtenidos, y la cláusula USING proporciona el valor para la variable de enlace :empno.
  • Líneas 12-15: Mostrando los valores obtenidos.

Uso de SQL dinámico para DDL

PL/SQL estático no puede ejecutar DDL como CREATE, ALTER o DROP directamente. EXECUTE IMMEDIATE resuelve esto construyendo la instrucción como una cadena, lo cual también es útil cuando se proporciona un nombre de objeto en tiempo de ejecución:

DECLARE
   l_table_name VARCHAR2(30) := 'my_table';
   l_sql_stmt   VARCHAR2(200);
BEGIN
   l_sql_stmt := 'CREATE TABLE ' || l_table_name ||
                 ' (id NUMBER, name VARCHAR2(30))';
   EXECUTE IMMEDIATE l_sql_stmt;
END;
/

Los nombres de objetos (tabla, columna, esquema) no se pueden pasar como variables de enlace, por lo que deben concatenarse en la cadena. Valide siempre esta entrada, por ejemplo, con DBMS_ASSERT.SIMPLE_SQL_NAME, para evitar la inyección SQL.

DBMS_SQL para SQL dinámico

PL/SQL proporciona el paquete DBMS_SQL para trabajar con SQL dinámico cuando la estructura de la sentencia no se conoce hasta el tiempo de ejecución. El proceso de creación y ejecución del SQL dinámico implica los siguientes pasos:

  • ABRIR CURSOR: El SQL dinámico se ejecuta como un cursorPara ejecutar la sentencia SQL, primero debemos abrir el cursor.
  • ANALIZAR SQL: Analiza el SQL dinámico. Esto comprueba la sintaxis y mantiene la consulta lista para su ejecución.
  • Valores de VARIABLE DE ENLACE: Asigne los valores a las variables de enlace, si las hay.
  • DEFINIR COLUMNA: Defina cada columna utilizando su posición relativa en la instrucción select.
  • EJECUTAR: Ejecutar la consulta analizada.
  • OBTENER VALORES: Obtener los valores ejecutados.
  • CERRAR EL CURSOR: Una vez obtenidos los resultados, cierre el cursor.

Ejemplo 1: En este ejemplo, recuperamos los datos de la tabla emp para el emp_no '1001' mediante una instrucción DBMS_SQL. El bloque EXCEPTION cierra el cursor incluso si se produce un error.

DBMS_SQL para SQL dinámico

DECLARE
   lv_sql            VARCHAR2(500);
   lv_emp_name       VARCHAR2(50);
   ln_emp_no         NUMBER;
   ln_salary         NUMBER;
   ln_manager        NUMBER;
   ln_cursor_id      NUMBER;
   ln_rows_processed NUMBER;
BEGIN
   lv_sql := 'SELECT emp_name, emp_no, salary, manager FROM emp WHERE emp_no = :empno';
   ln_cursor_id := DBMS_SQL.OPEN_CURSOR;
   DBMS_SQL.PARSE(ln_cursor_id, lv_sql, DBMS_SQL.NATIVE);
   DBMS_SQL.BIND_VARIABLE(ln_cursor_id, ':empno', 1001);
   DBMS_SQL.DEFINE_COLUMN(ln_cursor_id, 1, lv_emp_name, 50);
   DBMS_SQL.DEFINE_COLUMN(ln_cursor_id, 2, ln_emp_no);
   DBMS_SQL.DEFINE_COLUMN(ln_cursor_id, 3, ln_salary);
   DBMS_SQL.DEFINE_COLUMN(ln_cursor_id, 4, ln_manager);
   ln_rows_processed := DBMS_SQL.EXECUTE(ln_cursor_id);
   LOOP
      IF DBMS_SQL.FETCH_ROWS(ln_cursor_id) = 0 THEN
         EXIT;
      ELSE
         DBMS_SQL.COLUMN_VALUE(ln_cursor_id, 1, lv_emp_name);
         DBMS_SQL.COLUMN_VALUE(ln_cursor_id, 2, ln_emp_no);
         DBMS_SQL.COLUMN_VALUE(ln_cursor_id, 3, ln_salary);
         DBMS_SQL.COLUMN_VALUE(ln_cursor_id, 4, ln_manager);
         DBMS_OUTPUT.PUT_LINE('Employee Name: '   || lv_emp_name);
         DBMS_OUTPUT.PUT_LINE('Employee Number: ' || ln_emp_no);
         DBMS_OUTPUT.PUT_LINE('Salary: '          || ln_salary);
         DBMS_OUTPUT.PUT_LINE('Manager ID: '      || ln_manager);
      END IF;
   END LOOP;
   DBMS_SQL.CLOSE_CURSOR(ln_cursor_id);
EXCEPTION
   WHEN OTHERS THEN
      DBMS_SQL.CLOSE_CURSOR(ln_cursor_id);
END;
/

Resultado

Employee Name  : XXX
Employee Number: 1001
Salary         : 15000
Manager ID     : 1000

Code Explicación:

  • Líneas 1-8: Declaración de variables.
  • Línea 10: Cómo formular la sentencia SQL.
  • Línea 11: Se abre el cursor mediante DBMS_SQL.OPEN_CURSOR, que devuelve el ID del cursor abierto.
  • Línea 12: Una vez abierto el cursor, se analiza la consulta SQL.
  • Línea 13: El valor de enlace '1001' se asigna en lugar de ':empno'.
  • Líneas 14-17: Definiendo las columnas por su posición relativa: (1) emp_name, (2) emp_no, (3) salary, (4) manager.
  • Línea 18: Ejecutar la consulta con DBMS_SQL.EXECUTE, que devuelve el número de registros procesados.
  • Líneas 19-32: La función FETCH_ROWS recupera los registros en un bucle. Cuando no quedan filas, devuelve 0, lo que finaliza el bucle.
  • Bloque de EXCEPCIÓN: Garantiza que el cursor esté cerrado para que los cursores abiertos no generen fugas de memoria si se produce un error.

NDS vs DBMS_SQL: ¿Cuándo usar cuál?

Ambos enfoques ejecutan SQL en tiempo de ejecución, pero se adaptan a situaciones diferentes:

  • Utilice SQL dinámico nativo (EXECUTE IMMEDIATE / OPEN-FOR) Cuando se conocen el número y los tipos de datos de las entradas y salidas en tiempo de compilación, es más rápido, más fácil de leer y requiere menos código.
  • Utilice DBMS_SQL cuando la estructura es desconocida hasta el tiempo de ejecución, por ejemplo, una consulta cuyo número de columnas seleccionadas o variables de enlace varía, conocido como SQL dinámico de método 4, o una instrucción demasiado grande para caber en una sola variable VARCHAR2 de 32K.

Preguntas Frecuentes

Las variables de enlace transmiten la entrada del usuario como datos, nunca como código ejecutable. La cláusula USING proporciona valores posicionalmente, por lo que el texto malicioso no puede alterar la estructura de la instrucción. Siempre enlace la entrada no confiable en lugar de concatenarla.

No. Oracle Solo enlaza valores de datos, no nombres de objetos. Concatene los identificadores en la cadena SQL y valídelos con DBMS_ASSERT.SIMPLE_SQL_NAME para evitar inyecciones.

EXECUTE IMMEDIATE recupera solo una fila. Para varias filas, abra un REF CURSOR con la instrucción OPEN-FOR, luego recorra FETCH hasta %NOTFOUND y CIERRE el cursor.

Agregue una cláusula RETURNING a INSERT, UPDATE o DELETE, y luego use la cláusula RETURNING INTO de EXECUTE IMMEDIATE para capturar los valores de las filas afectadas en los argumentos de enlace.

El SQL dinámico agrega sobrecarga de análisis porque las sentencias se compilan en tiempo de ejecución. Reutilizar variables de enlace permite Oracle compartir cursores y reducir análisis sintácticos difíciles, mantenerping Rendimiento similar al de SQL estático.

La cadena debe ser VARCHAR2 o CHAR. No se permiten caracteres nacionales como NVARCHAR2 y NCHAR. Para textos de más de 32 KB, DBMS_SQL acepta una colección de fragmentos VARCHAR2.

Sí. Los asistentes de IA, como GitHub Copilot, elaboran bloques EXECUTE IMMEDIATE y DBMS_SQL a partir de indicaciones sencillas, sugieren marcadores de posición para variables de enlace y explican cada cláusula, aunque un desarrollador debería revisar el resultado.

Los escáneres de código impulsados ​​por IA señalan la entrada de usuario concatenada y recomiendan variables de enlace o comprobaciones DBMS_ASSERT. Resaltan patrones riesgosos durante la revisión, ayudan aping Los equipos detectan fallos de inyección antes de la implementación.

Resumir este post con: