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 @