Data

7 jan, 2009

MySQL – Variáveis Locais e Tratamento de Erros em Stored Procedures

Publicidade

Continuando com os artigos que esclarecem e explicitam o conhecimento relacionado com a criação dos procedimentos armazenados em um servidor de bancos de dados MySQL, desta vez, com esta série de artigos, temos o objetivo de apresentar as questões relacionadas com a criação de variáveis locais, escopo de variáveis e o tratamento de erros dentro de Stored Procedures. Nesta primeira parte, daremos ênfase aos conceitos envoltos à criação de variáveis locais e o seu escopo de visibilidade.

Para quem ainda não leu os artigos anteriores, publicados aqui no Portal iMasters, sobre Stored Procedures, sugiro dar uma passada por lá antes de avançar a este tópico que será apresentado.

Os artigos anteriores são:

Stored Procedures no MySQL

Stored Procedures – Transações

Stored Procedures e Variáveis do Usuário

Após ler este artigo, você estará apto a:

  • Definir o que são variáveis locais no MySQL;
  • Como criar variáveis locais em meio aos Stored Procedures e seus respectivos escopos;
  • Utilizar a declaração DECLARE para declaração de variáveis locais;
  • Utilizar as declarações SET e INTO para atribuição de valores a variáveis locais previamente declaradas.

Variáveis Locais

O primeiro ponto a ser explorado nesta série de artigos serão as questões envolvendo as variáveis locais. Você pode ter acabado de ler o artigo em que foram mencionadas as variáveis do usuário ou mesmo user variables. Estas são diferentes, têm seus escopos definidos de forma diferente das variáveis locais. Uma variável local somente será válida durante a execução de um procedimento armazenado, seja ele uma Stored Procedure, uma Trigger ou Stored Function, sendo que, após o término da execução de tais procedimentos, esta variável é destruída da memória[1], juntamente com seu respectivo valor.

(UFMA/2007) [1] A variável é uma abstração da célula de memória[/b]. Imagine agora que o programador, ao invés de utilizar os endereços físicos da memória para identificar as células que usa, tenha à sua disposição rótulos simbólicos com nomes intuitivos. Ao invés de utilizar o endereço (posição da célula dentro da memória), o valor em questão é identificado através desse rótulo (ou nome) simbólico – algo como var ou var -, escolhido com auxílio do bom senso do programador. Sempre que for necessário referir-se a algum dado, o programador utiliza o rótulo associado à variável que contém o mesmo. O compilador realiza automaticamente a transformação dos rótulos em endereços de memória, sem que seja necessário nos preocuparmos com isso. A célula de memória, quando identificada por um rótulo, será denominada uma variável. Ao invés de pensar na memória como uma seqüência longa de células, podemos entender a memória como um repositório que contém as variáveis associadas ao programa. Após a execução deste programa, ele e as respectivas células que contêm as suas variáveis e valores são removidos da memória central e estas células são entregues para que o kernel realoque novos procedimentos.

Uma variável, para ser declarada, precisa estar entre os chamados compound statements, ou comandos aninhados dentro de um procedimento qualquer, que por sua vez também são chamados de Stored Routines. Provavelmente você já deve ter visto em um dos artigos anteriores supracitados, a utilização de BEGIN ... END para dar início à escrita da lógica do procedimento. Para se declarar uma variável local, é necessário estarmos posicionados entre BEGIN ... END, como mostra o exemplo abaixo:

mysql> DELIMITER //
mysql> CREATE PROCEDURE SP_TEST(IN num INT)
    -> BEGIN
    ->   DECLARE x INT DEFAULT 0;
    ->   SET x = num;
    -> END;
    -> //
Query OK, 0 rows affected (0.00 sec)

O Stored Procedure ou programa implementado acima é um procedimento muito simples, no qual utilizamos os comandos BEGIN ... END para aninhar outros comandos que são parte da lógica do programa, tais como a declaração da variável local, utilizando a declaração DECLARE e a atribuição do valor enviado ao procedimento como parâmetro à variável x com a declaração SET. Sabemos então que DECLARE é utilizado para declaração de variáveis locais e SET para atribuição de valores a tais variáveis.

Um detalhe a ser observado é que o valor inicial de uma variável local é NULL, logo que esta é declarada sem um valor padrão. Como uma comparação de qualquer valor com NULL é igual a NULL, muitos DBA’s e programadores passam horas em frente à implementação de um procedimento e perdem bastante tempo por não tomar os devidos cuidados na declaração das variáveis locais. Com a intenção de tornar o valor inicial da variável x igual a 0 (zero), que a cláusula DEFAULT, que é opcional, foi abordada. A seguir, mostro a criação de um procedimento que compara um valor a uma variável declarada mas ainda sem um valor atribuído.

mysql> DELIMITER //
mysql> CREATE PROCEDURE SP_TEST_1(IN num INT)
    -> BEGIN
    ->   DECLARE x INT;
    ->   IF(num IS NOT NULL) THEN
    ->     SELECT (num = x) AS "Compare!" ;
    ->   END IF;
    -> END;
    -> //
Query OK, 0 rows affected (0.00 sec)

mysql> CALL SP_TEST_1(1);
----------
 Compare! 
----------
     NULL 
----------
1 row in set (0.00 sec)

Query OK, 0 rows affected (0.02 sec)

Como um valor não foi setado internamente para a variável x, a comparação lógica interna ao procedimento, no contexto atual, foi algo como 1 = NULL que é igual a NULL, quando que, se colocarmos a cláusula DEFAULT com o valor 0, o valor inicial de x será igual a 0 (zero).

mysql> DELIMITER //
mysql> CREATE PROCEDURE SP_TEST_1(IN num INT)
    -> BEGIN
    ->   DECLARE x INT DEFAULT 0;
    ->   IF(num IS NOT NULL) THEN
    ->     SELECT (num = x) AS "Compare!" ;
    ->   END IF;
    -> END;
    -> //
Query OK, 0 rows affected (0.00 sec)

mysql> CALL SP_TEST_1('');
----------
 Compare! 
----------
        1 
----------
1 row in set (0.00 sec)

Internamente, um valor NULL é enviado ao parâmetro num que é obtido a partir da conversão de NULL para o valor DEFAULT para um tipo INT que é 0 (zero), para colunas de tabelas e parâmetros, assim, a comparação retorna 1 (um) que é igual a true, na comparação entre o parâmetro num e a variável local x.

Caso precisemos declarar mais de uma variável local, em meio a um procedimento, colocamos todas elas juntas em uma mesma linha, separadas por vírgula, como exibido abaixo:

mysql> DELIMITER //
mysql> CREATE PROCEDURE SP_VARIAVEIS()
    -> BEGIN
    ->   DECLARE x, y, z INT DEFAULT 0;
    ->   SELECT x AS "VAR 1", y AS "VAR 2", z AS "VAR 3";
    -> END;
    -> //
Query OK, 0 rows affected (0.00 sec)

mysql> CALL SP_VARIAVEIS();
---------------------
 VAR 1  VAR 2  VAR 3 
---------------------
     0      0      0 
---------------------
1 row in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

Variáveis locais não são case sensitives, ou seja, VAR, var, VaR e vAr são a mesma coisa. O que devemos nos atentar é quanto ao seu escopo.

Escopo de Variáveis Locais

O escopo das variáveis locais no MySQL é definido pela sua posição, aninhada em blocos de uma procedimento armazenado no servidor de bancos de dados MySQL. Vejamos um exemplo:

mysql> DELIMITER //
mysql> CREATE PROCEDURE SP_ESCOPO()
    -> BEGIN
    ->   DECLARE x INT DEFAULT 0;
    ->   BEGIN
    ->     DECLARE x INT DEFAULT 0;
    ->     BEGIN
    ->       DECLARE x INT;
    ->     END;
    ->     SET x = NULL;
    ->   END;
    ->   SELECT x AS Var;
    -> END;
    -> //
Query OK, 0 rows affected (0.00 sec)

A questão é mesmo o entendimento, antes mesmo de executar qual será o valor enviado ao usuário pela consulta SELECT que finaliza o procedimento. Na verdade, temos algumas variáveis no corpo do Stored Procedure, todas elas com o mesmo nome – o que é muito negligente por parte do desenvolvedor. Perceba que temos também vários blocos de código aninhados – compound statements. No primeiro deles declaramos a primeira variável x com o valor definido inicialmente como 0 (zero), por atribuirmos a cláusula DEFAULT, com um valor inicial igual a 0 (zero), caso um não seja atribuído.

Caso mudemos o SELECT para um bloco mais um interno ao procedimento, ele retornará outro valor. Chegamos à conclusão então que, uma variável terá seu escopo definido somente dentro do bloco a que ela pertence. Uma vez finalizado o bloco, ela não terá mais visibilidade, sendo destruída, retirada da memória.

A execução do procedimento é a que segue:

mysql> CALL SP_ESCOPO();
------
 Var  
------
    0 
------
1 row in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

Podemos atribuir um valor a uma variável local, em meio aos procedimentos armazenados no servidor de bancos de dados MySQL, através da declaração INTO. Com ela, podemos buscar um valor contido em uma coluna de uma tabela e atribuí-lo a uma variável local previamente declarada, como é exibido a seguir:

mysql> DELIMITER //
mysql> CREATE PROCEDURE SP_INTO()
    -> BEGIN
    ->   DECLARE x INT DEFAULT 0;
    ->   SELECT 1 INTO x;
    ->   SELECT x;
    -> END;
    -> //
Query OK, 0 rows affected (0.00 sec)

Não enviamos nenhum parâmetro para realmente focar na operação lógica, interna ao procedimento. Perceba que, declaramos a variável x e depois com um SELECT, atribuímos um valor para ela. Após isto, externamos ao procedimento o valor atribuído com a cláusula INTO a x.

Por fim, a execução:

mysql> CALL SP_INTO();
------
 x    
------
    1 
------
1 row in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

Conclusão

Vimos, neste artigo, como trabalhar com variáveis locais internas aos procedimentos armazenados utilizando os compound statements, como declarar as variáveis com a declaração DECLARE, verificar o escopo da variável e sua visibilidade através dos blocos de código, declarar mais de uma variável em uma mesma linha, poupando linhas e escrita de código SQL, a atribuição de valores às variáveis através das declarações SET e INTO e a execução dos mais variados Stored Procedures.

Na continuação desta série, veremos como declarar variáveis locais que serão “encarregadas” de levantar erros em meio aos procedimentos, dando outra continuidade para o fluxo do programa ou mesmo finalizando sua execução, que são os HANDLERs para tratamento de erros.

Happy MySQL’ing!