DB2 – Sentencias SQL Dinámicas en Procedimientos Almacenados.

En algunas ocasiones debemos armar consultas a partir de condiciones muy específicas y  cuando estamos desarrollando del lado del cliente esto es una tarea muy sencilla pues los lenguajes de programación generalmente arman la sentencia y se la envían ya formada al servidor de bases de datos pero, ¿qué hacer si la consulta debe ser armada de manera dinámica en un procedimiento almacenado?. Bueno, para ello podemos hacer uso de la cláusula prepare que como ya vimos en el artículo  Borrado Dinámico de Procedimientos almacenados,  nos permite crear una sentencia SQL reconocible por el servidor que puede ser ejecutada.

Así que en este ejemplo vamos a crear un procedimiento almacenado que arme una sentencia SQL de forma dinámica y devuelva el resultado de su ejecución.   Comenzaremos declarando el encabezado de nuestro procedimiento:

CREATE PROCEDURE DATABASENAME.SPSQLDINAMIC()
  SPECIFIC SPSQLDINAMIC
  NOT DETERMINISTIC
  RESULT SET 1

luego vamos a definir las variables que almacenaran la sentencia SQL:

  DECLARE DIN_SQL VARCHAR ( 1000 ) ; -- variable que almacena la sentencia SQL
  DECLARE VARCOLUMNA VARCHAR(100); -- Almacena el valor a mostrar

y también vamos a declarar el cursor que nos permitirá dar salida al dataset resultante de la ejecución de la sentencia SQL

  DECLARE CURSORDINAMICO CURSOR WITH RETURN
      FOR DIN_SQL;

como puede observarse, la variable DIN_SQL que almacenará la sentencia SQL es referenciada directamente por el cursor y esto se debe a que como mencioné anteriormente, la cláusula prepare convierte el contenido de la variable en una setencia SQL reconocible por el servidor que puede ser ejecutada  de manera directa (en este caso por la declaración del cursor).

Ahora vamos a establecer los valores de las variables para armar nuestra sentencia SQL:

   SET VARCOLUMNA = '''CADENA DINÁMICA'' COLUMNA';
   SET DIN_SQL = ' SELECT ' || VARCOLUMNA || ' FROM SYSIBM.SYSDUMMY1';

como podemos ver VARCOLUMNA almacena el valor del campo columna que es concatenado al SELECT contenido en la variable DIN_SQL que nos resultará después de la concatenación en una sentencia sql de la siguiente forma:

SELECT 'CADENA DINÁMICA' COLUMNA
 FROM SYSIBM.SYSDUMMY1

Y por último vamos a establecer los bloques que preparan la sentencia y la ejecutan para devover el dataset resultante:

    PREPARE DIN_SQL FROM DIN_SQL;
    OPEN CURSORDINAMICO;

¿Muy sencillo verdad?, ahora si lo necesitas aquí está el código completo del procedimiento almacenado:

CREATE PROCEDURE DATABASENAME.SPSQLDINAMIC()

  SPECIFIC SPSQLDINAMIC
  NOT DETERMINISTIC
  RESULT SET 1

BEGIN

  DECLARE DIN_SQL VARCHAR ( 1000 ) ;
  DECLARE VARCOLUMNA VARCHAR(100);
  DECLARE CURSORDINAMICO CURSOR WITH RETURN
      FOR DIN_SQL;

  SET VARCOLUMNA = '''CADENA DINÁMICA'' COLUMNA';
  SET DIN_SQL = ' SELECT ' || VARCOLUMNA || ' FROM SYSIBM.SYSDUMMY1';

  PREPARE DIN_SQL FROM DIN_SQL;
  OPEN CURSORDINAMICO;

END;

y para ejecutarlo seria de la siguiente manera:

CALL DATABASENAME.SPSQLDINAMIC();

8 Comments

  1. Muy buenas,

    gracias por el post, pero me surge la duda de como podria incluir en el mismo proceso un bloque de inserts en otra tabla para hacer una inserción por cada registro debuelto en ese select.

      • Estimado Amigo me podras ayudar, me sale un error al momento de ejecutar el select. Gracias de antemano.

        BEGIN
        DECLARE FIL_OPERACION VARCHAR ( 50 ) ;
        DECLARE RPTINDICE CURSOR WITH RETURN FOR

        SELECT A . CLIENTE , A . IDOPE , A . ANIO , A . MES , B . CLINOM , C . DESCRIPCION FROM SPEED400AT . PO_PARTEREPORTE A
        INNER JOIN SPEED400AT . TCLIE B ON A . CLIENTE = B . CLICVE
        INNER JOIN SPEED400AT . PO_OPERACIONES C ON A . IDOPE = C . ID
        WHERE ANIO = @AÑO AND MES = @MES || FIL_OPERACION GROUP BY A . CLIENTE , A . IDOPE , A . ANIO , A . MES , B . CLINOM , C . DESCRIPCION ORDER BY A . ANIO ASC , A . MES ASC ;

        IF @OPERACION > 0 THEN
        SET FIL_OPERACION = ‘ AND A.IDOPE = ‘ || @OPERACION ||” ;
        INSERT INTO SPEED400AT . TMP_VALORES_STORE ( VALOR ) VALUES ( FIL_OPERACION ) ;
        else
        SET FIL_OPERACION = ‘ ‘ ;
        —–INSERT INTO SPEED400AT . TMP_VALORES_STORE ( VALOR ) VALUES ( ‘ var opera ‘ ) ;
        END IF ;
        OPEN RPTINDICE ;
        END ;

  2. Estimados, necesito su ayuda. Tengo qeu auditar la base de datos DB2, el cliente me dice que me la entrega con la información unix, pero como puedo reconocer los procedimientos almacenados en esa base de datos.

  3. Hola.

    Muchas gracias por compartir tus excelentes conocimientos.
    Te quería hacer una pregunta. Cuando usamos el cursor con la clausula WITH RETURN FOR , pues ya sabemos que permitirá dar salida al dataset resultante de la ejecución de la sentencia SQL, pero a su vez el cursor queda en estado open , lo cual nos es muy bueno que queden demasiados cursores abiertos ya que se consumen la memoria de la base de datos. Estuve buscando en la documentación de IBM , y al parecer ellos dicen que estos cursores se cierran al finalizar el procedimiento. pero te quería consultar que de seguro por tu experiencia me puedes dar una mejor orientación para el manejo de cursores con retorno.

    quedo atento a los comentarios.
    Muchas gracias por tu tiempo.

Deja un comentario