A ideia de se trabalhar com transações foi uma grande invenção na história dos bancos de dados. Elas facilitam enormemente o controle de operações, além de garantirem a integridade dos dados.
Resumindo: frequentemente a dupla COMMIT/ROLLBACK salva a pátria!
O DB2, assim como muitos outros SGBDs, adota o AUTOCOMMIT como padrão. Ou seja, se o seu código não especifica um COMMIT, o DB2 entende que seu código contém uma transação implícita e conclui a transação quando o código chegar ao fim.
Ok, então se o código não tiver um COMMIT, a transação vai ser concluída com sucesso. Mas se você escrever o COMMIT no lugar errado, pode ter dores de cabeça.
Vejamos o exemplo de um procedimento armazenado que transfere dados entre as tabelas TABELA1 e TABELA2 (script completo – em sua forma original – é apresentado no final desta página). Observe que você deve salvar essas declarações em dois arquivos separados e depois rodar cada um deles. Primeiro crie as tabelas (arquivo criatabelas.ddl) e depois crie a rotina (arquivo meuscript.db2).
$ db2 -tvf criatabelas.ddl $ db2 -td@ -vf meuscript.db2
Em seguida, execute a procedure e veja o resultado na tabela.
$ db2 "CALL meuesquema.procedimento1 ('2013-01-22 00:00:00', '2013-03-30 00:00:00')"
Return Status = 0
$ db2 "select * from meuesquema.tabela2"
CAMPO1 CAMPO2 CAMPO3
-------------------------------- -------------------------------------------------- --------------------------
0 record(s)
Pois é, meu amigo. Quando viu o “Return Status = 0” como resultado da chamada da rotina, você acreditou que ela tinha rodado corretamente, certo? Se isso te consola, saiba que você não é o primeiro e nem o último em cair nessa cilada.
O que acontece é que o código da rotina contém dois problemas. Teoricamente, toda rotina do DB2 que altera dados (INSERT, UPDATE, DELETE) deve incluir a cláusula MODIFIES SQL DATA. Assim, as primeiras linhas da rotina (linhas 9 a 18 da listagem) devem ser substituídas pelas linhas a seguir:
CREATE PROCEDURE meuesquema.procedimento1 (
startDate TIMESTAMP,
endDate TIMESTAMP)
LANGUAGE SQL
MODIFIES SQL DATA
DYNAMIC RESULT SETS 1
BEGIN
O segundo erro é a colocação do COMMIT, que deveria ser o último comando da rotina. Portanto, as linhas finais da rotina (61 a 72) também devem ser alteradas. Uma forma de fazer isso é substituindo aquelas linhas pelas que apresento a seguir:
IF intError > 0 THEN
ROLLBACK;
RETURN intError ;
END IF;
COMMIT;
END
@
Para testar o novo código, altere o arquivo meuscrip.db2, apague a rotina existente, recrie a rotina com o novo script e execute-a novamente. Aí sim você verá sua transação sendo executada corretamente!
$ db2 -td@ -vf meuscript.db2
DB20000I The SQL command completed successfully.
$ db2 "CALL meuesquema.procedimento1 ('2013-01-22 00:00:00', '2013-03-30 00:00:00')"
Return Status = 0
$ db2 "select * from meuesquema.tabela2"
CAMPO1 CAMPO2 CAMPO3
------------------------- ----------------------- --------------------------
WAGNER wcrivelini 1900-01-01-00.00.00.00000
1 record(s) selected.
São dicas simples, mas evitam que você tenha problemas sérios com suas rotinas.
Por hoje é só.
-- 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 contendo erro na definição da transaçã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
COMMIT;
RETURN 0;
ELSE
ROLLBACK;
RETURN intError ;
END IF;
END
@




