SQL Dinâmico

 

O SQL dinâmico permite que se crie procedimentos de propósito mais geral. Por exemplo, utilizando o SQL dinâmico você pode escrever um procedimento que opere sobre uma tabela cujo nome só será conhecido em tempo de execução. Ela permite ainda o uso da linguagem de definição de dados (DDL), isto é, permite que se crie (CREATE), altere (ALTER) e remova (DROP) objetos dentro de um procedimento ou bloco PL/SQL, o que não é permitido com declarações do SQL estático.

O SQL dinâmico é implementado pela package DBMS_SQL.

 

Usando a DBMS_SQL

Para que uma declaração SQL dinâmico possa ser processada é preciso que haja um cursor que guarde o identificador da declaração, este cursor (do tipo INTEGER) é diferente dos cursores convencionais da PL/SQL e é usado apenas pela biblioteca DBMS_SQL. O identificador é atribuído ao cursor pela função OPEN_CURSOR.

O próximo passo é se analisar gramaticalmente a declaração, utilizando o procedimento PARSE, ele analisa a sintaxe da consulta e a associa ao cursor. Caso a declaração analisada seja uma DDL ela será executada pelo procedimento e um commit implícito também será executado, portanto não é necessário executar o procedimento EXECUTE.

Se a declaração possuir referências a variáveis de ambiente, então é necessário que se insira um marcador para estas variáveis dentro da declaração, colocando o sinal (:) antes da variável, como em :nome. Depois que a declaração foi analisada pode-se ligar o valor às variáveis utilizando o procedimento BIND_VARIABLE, e quando a declaração for executada as variáveis serão substituídas pelos respectivos valores.

As colunas selecionadas por uma declaração SELECT são identificadas por suas posições relativas dentro da declaração. Em uma consulta você deve usar o procedimento DEFINE_COLUMN para especificar quais as variáveis que receberão que serão gerados quando a consulta for executada. Para colunas do tipo LONG é utilizado o procedimento DEFINE_COLUMN_LONG.

A declaração é executada pela função EXECUTE. Caso a declaração seja INSERT, UPDATE ou DELETE a função retorna o número de linhas processadas, caso contrário seu valor pode ser desprezado.

Se você definiu uma declaração SELECT então os valores retornados pela consulta podem ser extraídos do cursor utilizando-se a função FETCH_ROWS, esta função retorna 0 (zero) caso não haja mais registros a serem extraídos do cursor.

Se a declaração for uma consulta, os valores obtidos como resultado podem ser atribuídos a variáveis usando o procedimento COLUMN_VALUE ou COLUMN_VALUE_LONG para colunas do tipo LONG, caso a declaração seja uma chamada a um bloco PL/SQL, então o valor retornado por este bloco pode ser obtido com o procedimento VARIABLE_VALUE.

Quando você não for mais utilizar o cursor, ele deve ser fechado com o procedimento CLOSE_CURSOR para evitar que o bloco da memória não permaneça desnecessariamente alocada.

 

Procedimentos, Funções e Parâmetros

BIND_VARIABLE(cursor,variável,valor,[tamanho]) Permite a atribuição de um valor à variáveis de ambiente, tamanho é utilizado para variáveis do tipo VARCHAR2.
CLOSE_CURSOR(cursor) Fecha o cursor e libera a de memória associada.
COLUMN_VALUE(cursor,posição,variável[,erro][,tamanho]) Recupera um valor do cursor para a variável.
DEFINE_COLUMN(cursor,posição,variável) Associa colunas ou expressões da consulta com a variável.
EXECUTE(cursor) retorna INTEGER Executa a declaração associada ao cursor e retorna o número de linhas processadas.
FETCH_ROWS(cursor) retorna INTEGER Extrai do cursor os valores resultantes da consulta, retorna 0 (zero) caso não haja mais linhas.
IS_OPEN(cursor) retorna BOOLEAN Retorna TRUE se o cursor já estiver aberto e FALSE caso contrário.
OPEN_CURSOR retorna INTEGER Esta função "abre" o cursor e retorna um identificador para ele.
PARSE(cursor,declaração,flag) Analisa a declaração. flag especifica o comportamento que o Oracle terá durante a análise, pode ser: DBMS_SQL.V6 (para Versão 6), DBMS_SQL.V7 (para Versão 7) e DBMS_SQL.NATIVE (para utilizar o comportamento de acordo com a base à qual ele está conectado)
VARIABLE_VALUE (cursor,variável_da_declaração,variável,[tamanho]) Recupera o valor da variável_da_declaração para a variável do bloco PL/SQL.

 

Usando SQL Dinâmico para Chamar Stored Procedures e Functions

Você pode também utilizar o SQL dinâmico para chamar stored procedures e functions de dentro de seu bloco PL/SQL. Para isto os passos são os seguintes:

  1. Chamar DBMS_SQL.OPEN_CURSOR para abrir um cursor;
  2. Se existir variáveis bind, vincule-as utilizando a procedure DBMS_SQL.BIND_VARIABLE;
  3. Execute a procedure ou function utilizando DBMS_SQL.EXECUTE;
  4. Se houver variáveis bind, chame DBMS_SQL.VARIABLE_VALUE para obter os valores associados a elas;
  5. Feche o cursor com a procedure DBMS_SQL.CLOSE_CURSOR.

Quando você utiliza a package DBMS_SQL para executar uma procedure ou function as variáveis passadas como parâmetro são tratadas como sendo bind, o mesmo acontece para variáveis que recebem os valores retornados por functions.

As variáveis associadas a parâmetros do tipo IN ou IN OUT devem ter valores associados antes que eles sejam passados para a procedure ou function. As variáveis do tipo VARCHAR2 ou CHAR que serão vinculadas a parâmetros do tipo OUT, devem ser inicializadas ou seu tamanho máximo deve ser passado para a procedure DBMS_OUTPUT.BIND_VARIABLE através do parâmetro tamanho.

 


Exemplos de SQL Dinâmico

 

Exemplo 1

O exemplo seguinte cria um procedimento (stored procedure) que realiza uma consulta em uma coluna de uma tabela, o nome da coluna e da tabela são passados como parâmetro para o procedimento.


CREATE OR REPLACE PROCEDURE SQL_DINAMICO(coluna IN VARCHAR2, tabela IN VARCHAR2) IS

	cur_handle INTEGER;
	texto VARCHAR2(80);
	temp NUMBER;

BEGIN

	cur_handle := DBMS_SQL.OPEN_CURSOR;

	DBMS_SQL.PARSE(cur_handle, 'SELECT '||coluna||' FROM '|| tabela, DBMS_SQL.V7);

	DBMS_SQL.DEFINE_COLUMN(cur_handle, 1, texto, 80);

	temp := DBMS_SQL.EXECUTE(cur_handle);

	LOOP
		IF DBMS_SQL.FETCH_ROWS(cur_handle) = 0 
			THEN EXIT;
		ELSE 
			DBMS_SQL.COLUMN_VALUE(cur_handle, 1, texto);
			DBMS_OUTPUT.PUT_LINE(texto);
		END IF;
	END LOOP;

	DBMS_SQL.CLOSE_CURSOR(cur_handle);
END SQL_DINAMICO;
/

Como neste caso a declaração SQL é uma consulta (SELECT) o valor da variável temp pode ser desprezado.

O procedimento pode ser criado através do SQL*Plus com o seguinte comando (supondo que o código esteja no arquivo EXEMPLO1.SQL) :

SQL> @EXEMPLO1.SQL
 

Este procedimento pode ser utilizado dentro de outros procedimentos ou blocos PL/SQL, ou pode ainda ser executado no SQL*Plus com o comando:

SQL> EXECUTE SQL_DINAMICO('ENAME','EMP')

que irá retornar os dados da coluna ENAME da tabela EMP.


Exemplo 2

Este segundo exemplo mostra a utilização de variáveis de ambiente (bind variables) em declarações SQL. Os marcadores :valor1 e :valor2 são inseridos na declaração SQL para indicar que serão utilizados valores obtidos a partir de variáveis de ambientes (no exemplo são bind1 e bind2) e o procedimento BIND_VARIABLE faz a ligação entre estas variáveis e os marcadores .

DECLARE
	cur_handle INTEGER;
BEGIN
	cur_handle := DBMS_SQL.OPEN_CURSOR;
	DBMS_SQL.PARSE(cur_handle,
		       'INSERT INTO FONE VALUES (:valor1,:valor2)',
		       DBMS_SQL.V7);
	DBMS_SQL.BIND_VARIABLE(cur_handle,':valor1',:bind1);
	DBMS_SQL.BIND_VARIABLE(cur_handle,':valor2',:bind2);
	DBMS_OUTPUT.PUT_LINE('NUM. DE LINHAS INSERIDAS: '||DBMS_SQL.EXECUTE(cur_handle));
	DBMS_SQL.CLOSE_CURSOR(cur_handle);
END;
/

Para a declaração utilizada (INSERT), a função EXECUTE retorna o número de linhas inseridas.

As variáveis de ambiente poderiam ser ítens do Oracle Forms, seus valores poderiam ter sido atribuidos em outros blocos e procedimentos ou ainda podem ser difinidos a partir do SQL*Plus :

SQL> VARIABLE bind1 VARCHAR2(40)
SQL> VARIABLE bind2 VARCHAR2(40)
SQL> BEGIN
   1 :bind1 := 'Oracle do Brasil';
   2 :bind2 := 'http://www.oracle.com.br';
   3 end;
   4 / 

e o bloco pode ser executado fazendo:

SQL> @EXEMPLO2.SQL 

que irá inserir os dados na tabela FONE e emitir a mensagem:

NUM. DE LINHAS INSERIDAS: 1 
PL/SQL procedure successfully completed.

Exemplo 3

Uma utilização da package DBMS_SQL para tornar os procedimentos ainda mais genéricos é mostrada neste exemplo. Ele cria um procedimento que recebe como argumento a declaração SQL inteira, não apenas parte dela. As declarações podem ser de inserção, alteração ou remoção. Sua listagem é mostrada a seguir.

CREATE OR REPLACE PROCEDURE QUERY(query_in IN VARCHAR2) IS

	cur_handle INTEGER;
	temp NUMBER;

BEGIN
	cur_handle := DBMS_SQL.OPEN_CURSOR;
	DBMS_SQL.PARSE(cur_handle, query_in, DBMS_SQL.V7);
	temp := DBMS_SQL.EXECUTE(cur_handle);
	DBMS_SQL.CLOSE_CURSOR(cur_handle);
	DBMS_OUTPUT.PUT_LINE('Num. de linhas tratadas : '||temp);

END QUERY;
/

Novamente o procedimento deve ser criado com o comando :

SQL> @EXEMPLO3.SQL

e um exemplo de sua utilização é :

SQL> EXECUTE QUERY('INSERT INTO FONE VALUES (''DISCOVER TECHNOLOGY'',''(011) 853-0692'')')

que irá retornar a mensagem :

Num. de linhas tratadas : 1
PL/SQL procedure successfully completed.

Exemplo 4

Neste exemplo, a package DBMS_SQL é utilizada para chamar uma stored function que retorna um VARCHAR2 e recebe como parâmetro uma variável do tipo IN NUMBER.

CREATE OR REPLACE FUNCTION RETORNA_TEXTO (numero IN NUMBER) RETURN VARCHAR2 AS
   temp_var VARCHAR2(30);
BEGIN
   temp_var := 'O valor passado foi ' || TO_CHAR(numero);
   RETURN temp_var;
END;
/


DECLARE
   cursor_handle NUMBER;
   temp INTEGER;
   valor NUMBER := 1;
   -- Variável que receberá o valor retornado pela função é inicializada
   -- com 30 espaços em branco
   var_text VARCHAR2(30) := '                              ';
BEGIN
   cursor_handle := DBMS_SQL.OPEN_CURSOR;
   DBMS_SQL.PARSE(cursor_handle,
                  'BEGIN :x1:=RETORNA_TEXTO(:x2); END;',
                  DBMS_SQL.V7);
   DBMS_SQL.BIND_VARIABLE(cursor_handle, ':x1', var_text);
   DBMS_SQL.BIND_VARIABLE(cursor_handle, ':x2', valor);
   temp := DBMS_SQL.EXECUTE(cursor_handle);
   DBMS_SQL.VARIABLE_VALUE(cursor_handle, ':x1', var_text);
   DBMS_SQL.CLOSE_CURSOR(cursor_handle);
   DBMS_OUTPUT.PUT_LINE('var_text = ' || var_text);
END;
/

Para o caso em que a variável var_text não é inicializada, o bloco fica da seguinte forma:

DECLARE
   cursor_handle NUMBER;
   temp INTEGER;
   valor NUMBER := 1;
   var_text VARCHAR2(30);
BEGIN
   cursor_handle := DBMS_SQL.OPEN_CURSOR;
   DBMS_SQL.PARSE(cursor_handle,
                  'BEGIN :x1:=RETORNA_TEXTO(:x2); END;',
                  DBMS_SQL.V7);
   DBMS_SQL.BIND_VARIABLE(cursor_handle, ':x1', var_text, 30);
   DBMS_SQL.BIND_VARIABLE(cursor_handle, ':x2', valor);
   temp := DBMS_SQL.EXECUTE(cursor_handle);
   DBMS_SQL.VARIABLE_VALUE(cursor_handle, ':x1', var_text);
   DBMS_SQL.CLOSE_CURSOR(cursor_handle);
   DBMS_OUTPUT.PUT_LINE('var_text = ' || var_text);
END;
/

 

Retorna a página anterior.

 

* Material encontrado na Internet
http://saudino.home.ml.org
gsf@mandic.com.br