DataARTIGO

Deu erro por incompatibilidade de tipos de dados. E agora?

Veja o que fazer quando, ao criar um procedimento armazenado, dá erro por incompatibilidade de tipos de dados.

Dia destes eu estava criando um procedimento armazenado e, como sempre, focando minha atenção nas coisas que eu considerava mais importantes:

  1. A lógica de negócios;
  2. A estrutura adequada das declarações SQL para fazer uso dos índices de cada tabela;
  3. Tratamento de erros e controle da transação.

Depois de algum trabalho, meu procedimento estava pronto. Foi criado usando o Command Line Processor (CLP) para rodar no DB2 em LINUX 64-bits. A sintaxe desta rotina é mostrada no código ao final do artigo.

Salvei este código no arquivo meuscript.db2 e executei o comando para criação da rotina. Para quem não conhece DB2 CLP, o parâmetro “-td” especifica o caractér de identificação de instruções dentro de um procedimento armazenado.

db2 -td@ -vf  meuscript.db2

A rotina foi criada com sucesso. Em seguida, fui testá-la:

db2  "CALL meuesquema.procedimento1 ('2013-01-22 00:00:00', '2013-03-30 00:00:00')"

Foi aqui que tive uma surpresa, porque observei que minha transação nunca se completava (ROLLBACK). Rodando passo a passo as instruções da lógica de negócios, identifiquei uma mensagem de erro de SQL informando “tipos de dados incompatíveis”.

Examinando meu código com mais atenção, encontrei o problema. Observe que na instrução de INSERT eu especifiquei o uso da função COALESCE:

COALESCE(T.END_TIMESTAMP, '00:00:00')

Isso quer dizer o seguinte: se o campo END_TIMESTAMP for não nulo, use o seu valor. Do contrário, use o  valor “00:00:00”.

Note que o horário “00:00:00” parece um TIMESTAMP, mas não é. No DB2, o tipo de dados TIMESTAMP aceita valores entre 0001-01-01-00.00.00.000000000 e 9999-12-31-24.00.00.000000000.

Sendo assim, o correto seria alterar o valor da função COALESCE para usar o menor TIMESTAMP reconhecido:

COALESCE(T.END_TIMESTAMP, '0001-01-01-00:00:00')

Depois de recriar a rotina e repetir o teste, vemos que tudo passa a funcionar adequadamente. Até parece piada o tempo que perdemos depurando erros elementares, mas isso tem a haver com a nossa falta de atenção com os detalhes. Nos preocupamos muito com o que nos parece complexo: lógicas de negócio, processos, etc. Mas não costumamos dar a devida atenção aos detalhes, como sintaxes ou tipos de dados.

Então fica a dica: cuidado com os erros bobos. Isso pode comprometer sua produtividade.

-- script DDL (criatabelas.ddl)
-- criacao de tabelas
CREATE TABLE meuesquema.tabela1 (nome VARCHAR(50), userid VARCHAR(50), start_timestamp TIMESTAMP , end_timestamp TIMESTAMP ); 

INSERT INTO meuesquema.tabela1 VALUES ('wagner', 'wcrivelini', '2013-03-01 0:00:00', null); 

CREATE TABLE meuesquema.tabela2 (campo1 VARCHAR(50), campo2 VARCHAR(50), campo3 TIMESTAMP );

-- script da rotina (meuscript.db2)
--procedimento armazenado em questão
CREATE PROCEDURE meuesquema.procedimento1 (
	startDate 	TIMESTAMP,
	endDate  	TIMESTAMP)
LANGUAGE SQL
DYNAMIC RESULT SETS 1
BEGIN

--===============================================
--error handling
--===============================================

	DECLARE SQLSTATE	CHAR(5) DEFAULT '00000';
	DECLARE l_error      	CHAR(5) DEFAULT '00000';
	DECLARE intError    	INT;

	DECLARE CONTINUE HANDLER FOR NOT FOUND 
	SET l_error = '00000';

	DECLARE CONTINUE HANDLER FOR SQLEXCEPTION, SQLWARNING

	BEGIN
		SET l_error = SQLSTATE;

		IF SUBSTR(l_error, 1, 1) >= '5' AND SUBSTR(l_error, 1, 1) <= '9' THEN 
			RESIGNAL;
		END IF;
	END;

--===============================================
-- business logic
--===============================================
	INSERT INTO meuesquema.tabela2 (campo1, campo2, campo3)
	SELECT 	
		UPPER(T.NOME) AS CAMPO1, 
		T.USERID AS CAMPO2,
		COALESCE(T.END_TIMESTAMP, '00:00:00') AS CAMPO3
	FROM  meuesquema.tabela1 T
	WHERE T.START_TIMESTAMP BETWEEN startDate  AND endDate;

	SET l_error = SQLSTATE ;

	IF LEFT(l_error, 2 ) > '02' THEN
		SET intError = intError + 1 ;
	END IF;

--===============================================
-- finishing transaction
===============================================
	  IF intError > 0 THEN
        ROLLBACK;
        RETURN intError ;
    END IF;

    COMMIT;

END
@

Consultor Sênior na Microsoft, na área de Data Insights para América Latina. Especialista em bancos de dados, é colunista em diversos portais de TI do Brasil e do exterior, com mais de 100 artigos técnicos publicados. É também co-produtor do DatabaseCast, primeiro podcast brasileiro sobre bancos de dados.

Ver perfil