Dia destes eu estava criando um procedimento armazenado e, como sempre, focando minha atenção nas coisas que eu considerava mais importantes:
- A lógica de negócios;
- A estrutura adequada das declarações SQL para fazer uso dos índices de cada tabela;
- 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
@




