Triggers no SQLITE
Posted: março 6th, 2009 | Author: Carlan Calazans | Tags: aprendizado, dev, dica, pesquisa, sqlite, trigger | 1 Comment »Esta semana um grande amigo twitou uma pergunta que me chamou atenção. Já tinha estudado sobre o assunto a alguns anos atrás para utilizar em um sistema. Foi dai que veio a idéia para este post, peguei os meus rascunhos e comecei a escrever.
O banco de dados SQLITE, em sua versão atual, não possui suporte a integridade referencial. Nunca consegui entender o motivo, já que bancos similares como h2 e HSQLDB possuem. Talvez não implementaram a funcionalidade ainda por que existe uma outra alternativa. Uma forma de driblar essa limitação é através de triggers. Um trigger é disparado quando um evento ocorre. É possível deletar os registros filhos relacionados com uma tabela pai, por exemplo. O evento neste caso, para ficar bem claro, é a remoção de um registro da tabela pai.
A sintaxe básica para a criação de um trigger é:
{ BEFORE | AFTER | INSTEAD OF }
{ INSERT | UPDATE OF | DELETE }
ON { TABLE }
BEGIN
{ SQL STATEMENT }
END;
Para praticar, criei as tabelas abaixo. Se você for preguiçoso como eu, pode baixar o arquivo do banco de dados direto daqui. É um exemplo bem simples, se você já for um expert em banco de dados (ou um dba), nada do que vou mostrar é novidade.
-- users table
CREATE TABLE users (id integer PRIMARY KEY, login text, password text);
-- tasks table
CREATE TABLE tasks (id integer PRIMARY KEY, user_id integer, name text);
-- logs table
CREATE TABLE logs(id integer PRIMARY KEY, event text, created_at datetime DEFAULT (datetime('now', 'localtime')));
-- counts table
CREATE TABLE counts(id integer PRIMARY KEY, name text, value integer);
-- data
INSERT INTO "users" VALUES(1,'admin','admin');
INSERT INTO "tasks" VALUES(1,1,'Entender triggers no sqlite');
INSERT INTO "tasks" VALUES(2,1,'Aprender a voar');
INSERT INTO "counts" VALUES(1,'users',0);
INSERT INTO "counts" VALUES(2,'tasks',0);
COMMIT;
Agora vem a parte interessante. Vejamos como inserir um registro na tabela logs toda vez que um usuário for criado. O trigger é “colocado” na tabela users e, toda vez, após ocorrer um insert o trigger é disparado.
CREATE TRIGGER user_insert
after INSERT ON users
begin
INSERT INTO logs (event) VALUES ("New user created");
end;
Faça o teste. Termine de inserir outros usuários e verifique a tabela logs.
INSERT INTO "users" VALUES(3,'test','test');
SELECT * FROM logs;
O trigger acima monitora a operação insert e adiciona um registro em outra tabela. Agora um exemplo que monitora a mesma operação, mas realiza o update em outra tabela.
CREATE TRIGGER update_users_count
after INSERT ON users
begin
UPDATE counts SET value = value + 1 WHERE name = "users";
end;
-- test again
INSERT INTO "users" VALUES(4,'delete','me');
INSERT INTO "users" VALUES(5,'highlander','therecanbeonlyone');
Para o exemplo de integridade referencial, maiores informações no início do post, o trigger realiza o delete em todos os registros (for each row) relacionados com a tabela pai. Neste exemplo a tabela pai é users e a filha é tasks. A referência feita a old significa o valor atual do iterator no loop.
CREATE TRIGGER user_delete
before DELETE ON users
FOR each row
begin
DELETE FROM tasks WHERE tasks.user_id=old.id;
end;
-- test
INSERT INTO "tasks" VALUES(3,4,'Ganhar na mega-sena');
INSERT INTO "tasks" VALUES(4,4,'Delete me');
INSERT INTO "tasks" VALUES(5,4,'Dominar o mundo');
INSERT INTO "tasks" VALUES(6,3,'Test task');
INSERT INTO "tasks" VALUES(7,3,'Hello world');
INSERT INTO "tasks" VALUES(8,2,'Buy new car');
INSERT INTO "tasks" VALUES(9,2,'Drink more');
Delete o usuário de id número 4 e veja o que acontece.
Pra finalizar, recomendo a todos baixar o arquivo de exemplo e fuçar. Esta solução pode não atender a todos, já que para deletar 1000000 registros filhos, dependendo o seu hardware, o processo pode ser custoso. Não tenho um conhecimento tão profundo sobre engines de banco de dados para explicar como seria feito utilizando um banco com suporte a integridade referencial, portanto, leve isso em consideração ao interpretar a afirmação acima.
De qualquer forma, o uso do SQLITE só deve ser feito em protótipos ou em aplicações cujo os dados não são de muita importância. Caso os dados de sua aplicação sejam muito importantes, geralmente são, você deveria procurar outra alternativa.
Enfim, pelo menos, deu pra brincar um pouco.