quarta-feira, fevereiro 07, 2007

Raise trigger

As vezes acho que esse MySQL não pode estar
sendo usado para valer por empresas reais, do
mundo real, etc... ou eu que sou paranôico e
gosto de fazer uma grande parte da lógica dos
meus sistemas no banco de dados dividindo essa
responsabilidade com a aplicação. Não sei se
isso é certo, bom tema para uma discussão, quem
quizer pode postar aí o que acha. ;)

Bom estava eu aqui feliz da vida quando me de-
parei com um fato lamentável... no MySQL não
tenho como "matar" um gatilho, procedimento,
etc... Não há uma instrução semelhante a
RAISE_APPLICATION_ERROR do Oracle.

Caramba, e agora? Criar um procedimento que
trate os erros da aplicação? Insano! Não vou
escrever isso, isso tem que ser feito pelos de-
senvolvedores do SGBD e não por mim (po que má
vontade a minha :P)!

Mas a idéia até que por hora, apenas por hora,
soa como uma gambiarra (um bacalhau como eu
prefiro dizer) razoável. Mas mesmo assim seria
um trabalho insano, onde TODO o controle da
transação estaria na minha mão, e eu nem sei se
isso é possível!

Seria mais ou menos isso:

CREATE PROCEDURE x (INT parametro1 INT, OUT
resultado INT, OUT sqlError)
BEGIN

-- Digamos que eu tenha um select aqui dentro
-- e que o mesmo não possa vir 0 ou nulo em
-- um determinado campo.

-- Eu percorreria meu cursor e a cada linha
-- testaria se há valor no campo, se não ti-
-- ver eu setor meu sqlError com uma mensagem
-- de erro qualquer e pararia o loop.

-- Na aplicação eu teria que controlar se ele
-- é nulo, se for faz alguma coisa ou até
-- mesmo manda o banco dar commit\rollback no
-- procedimento e então poderia chamar uma
-- p_erros(IN mensagem, OUT erro); Que retor-
-- naria uma mensagem baseada em um código,
--etc...

END;

Fiquei cansado só de pensar em implementar isso
tudo.

Fica aqui um exemplo e como trabalhar com pro-
cedimentos e cursores.

Usando a base de dados TESTE que criamos:

mysql> DROP TABLE IF EXISTS Recado;

Saída no console: Query OK, 0 rows affected (0.00 sec)

mysql> DROP TABLE IF EXISTS Contato;

Saída no console: Query OK, 0 rows affected (0.00 sec)

mysql> DROP PROCEDURE IF EXISTS
P_RECADOSPORCONTATO;

Saída no console: Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> CREATE TABLE CONTATO(
CODCONTATO INT NOT NULL,
NOMECONTATO VARCHAR(100) NOT NULL
) ENGINE = INNODB;

Saída no console: Query OK, 0 rows affected (0.00 sec)

mysql> ALTER TABLE CONTATO ADD PRIMARY KEY (CODCONTATO);

Saída no console: Query OK, 0 rows affected
Records: 0 Duplicates: 0 Warnings: 0

mysql> ALTER TABLE CONTATO ADD INDEX (CODCONTATO);

Saída no console: Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0

mysql> DELIMITER //

mysql> CREATE TRIGGER BIF_CONTATO BEFORE INSERT ON CONTATO
FOR EACH ROW

BEGIN
DECLARE novoCodigo INT;

SELECT IFNULL(MAX(CodContato), 0) + 1
INTO novoCodigo
FROM Contato;

SET NEW.CodContato = novoCodigo;

END;
//

Saída no console: Query OK, 0 rows affected (0.00 sec)

mysql> CREATE TRIGGER BUF_CONTATO BEFORE UPDATE ON CONTATO
FOR EACH ROW

BEGIN

SET NEW.CodContato = OLD.CodContato;

END;
//

Saída no console: Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO Contato(CODCONTATO, NOMECONTATO) VALUES(0, 'Joao');

Saída no console: Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO Contato(CODCONTATO, NOMECONTATO) VALUES(0, 'Maria');

Saída no console: Query OK, 1 row affected (0.00 sec)

mysql> CREATE TABLE RECADO(
CODCONTATO INT NOT NULL,
RECADO VARCHAR(500) NOT NULL,
CODREMETENTE INT NOT NULL
) ENGINE = INNODB;

Saída no console: Query OK, 0 rows affected (0.00 sec)

mysql> ALTER TABLE RECADO ADD INDEX (CODCONTATO);

Saída no console: Query OK, 0 rows affected (0.02 sec)
Records: 0 Duplicates: 0 Warnings: 0

mysql> ALTER TABLE RECADO ADD FOREIGN KEY (CODCONTATO) REFERENCES CONTATO(CODCONTATO) ON DELETE CASCADE;

Saída no console: Query OK, 0 rows affected (0.00 sec)
Records: 0 Duplicates: 0 Warnings: 0

mysql> ALTER TABLE RECADO ADD FOREIGN KEY (CODREMETENTE) REFERENCES CONTATO(CODCONTATO) ON DELETE CASCADE;

Saída no console: Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0

mysql> INSERT INTO Recado(CodContato, Recado, CodRemetente) VALUES(2, 'Nao esquece do happy hour hoje!', 1);

Saída no console: Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO Recado(CodContato, Recado, CodRemetente) VALUES(1, 'Ok, irei sem falta.', 2);
Saída no console: Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO Recado(CodContato, Recado, CodRemetente) VALUES(2, 'É lá no buteco do Joaquin.', 1);
Saída no console: Query OK, 1 row affected (0.02 sec)

mysql> DELIMITER //

mysql> CREATE PROCEDURE P_RECADOSPORCONTATO(IN CodContato INT
, OUT total INT)

BEGIN
-- Variável que controla o loop
DECLARE linhaFinal INT DEFAULT 0;
-- Recebe o total de recados do contato
DECLARE qtdRecados INT DEFAULT 0;

DECLARE csRecados CURSOR FOR
SELECT COUNT(*)
FROM Recado re
WHERE re.CodContato = CodContato;

-- O controlador do fluxo
DECLARE CONTINUE HANDLER FOR NOT FOUND SET
linhaFinal = 1;


OPEN csRecados; -- Abre o cursor

-- Começa a varedura nos registros
looping: LOOP

-- Coloca na variável os dados da linha atual
FETCH csRecados INTO qtdRecados;

-- Controla para saber se tem que deixar o loop
IF linhaFinal = 1 THEN
LEAVE looping;
END IF;

SET total = qtdRecados; -- Seta uma variável
-- (neste caso um paâmetro)
-- com o resultado do
-- calculo da linha

END LOOP looping; -- Fecha o loop

END;
//

Saída no console: Query OK, 0 rows affected (0.00 sec)

mysql> DELIMITER ;

mysql> CALL P_RECADOSPORCONTATO(2, @resultado);

Saída no console: Query OK, 0 rows affected (0.00 sec)

mysql> SELECT @resultado;

Saída no console:
+------------+
| @resultado |
+------------+
| 2 |
+------------+
1 row in set (0.00 sec)

Em breve: funções.

Nenhum comentário: