Um gatilho é um comando SQL predefinido que é executado automaticamente quando ocorrem ações específicas no banco de dados. Ele pode ser disparado antes ou depois de um
INSERT
, UPDATE
, ou DELETE
evento. Os gatilhos são usados principalmente para manter a lógica do software no servidor MySQL e têm vários benefícios:
-
Os gatilhos ajudam a manter as operações globais centralizadas em um único local.
-
Eles reduzem o código do lado do cliente e ajudam a minimizar as viagens de ida e volta feitas ao servidor de banco de dados.
-
Eles ajudam a tornar os aplicativos mais escaláveis em diferentes plataformas.
Alguns casos de uso comuns de gatilhos incluem registro de auditoria, pré-computação de valores de banco de dados (por exemplo, somas cumulativas) e aplicação de regras complexas de validação e integridade de dados.
Neste guia, você aprenderá:
-
Como a sintaxe de um gatilho é estruturada.
-
Como criar gatilhos que são executados antes que outros eventos do banco de dados ocorram.
-
Como criar gatilhos que são executados após a ocorrência de outros eventos do banco de dados.
-
Como excluir gatilhos.
Antes de começar
-
Se você ainda não o fez, crie uma conta Linode e Compute Instance. Veja nossos guias de Introdução ao Linode e Criação de uma instância de computação.
-
Siga nosso guia Configurando e protegendo uma instância de computação para atualizar seu sistema. Você também pode definir o fuso horário, configurar seu nome de host, criar uma conta de usuário limitada e proteger o acesso SSH.
-
Um servidor e cliente MySQL instalado no servidor Linode. Guias de instalação para MySQL estão disponíveis para diferentes distribuições em nossa seção MySQL.
Prepare o banco de dados
Para entender melhor como os gatilhos funcionam, criaremos um banco de dados de exemplo e adicionaremos dados de exemplo nele. Mais tarde, criaremos diferentes gatilhos no banco de dados como um exercício de prova de conceito.
-
Primeiro, faça login no seu servidor MySQL:
mysql -u root -p
Em seguida, digite a senha root do seu servidor MySQL e pressione Enter para prosseguir.
-
Em seguida, você verá um prompt do MySQL semelhante ao mostrado abaixo:
mysql >
-
Crie umtest_database
executando o comando abaixo:
CREATE DATABASE test_database;
Saída:
Query OK, 1 row affected (0.02 sec)
-
Mude para o banco de dados:
USE test_database;
Saída:
Database changed
-
Uma vez selecionado o banco de dados, criaremos algumas tabelas que usaremos para demonstrar as triggers. Começaremos criando asstores
tabela. Esta tabela conterá informações sobre duas lojas/escritórios de amostra onde nosso negócio hipotético opera:
CREATE TABLE stores ( store_id BIGINT PRIMARY KEY AUTO_INCREMENT, store_name VARCHAR(50) ) ENGINE=InnoDB;
Saída:
Query OK, 0 rows affected (0.07 sec)
-
Em seguida, adicione dois registros àsstores
table executando os comandos abaixo:
INSERT INTO stores (store_name) VALUES ('Philadelphia'); INSERT INTO stores (store_name) VALUES ('Galloway');
Após cada comando, você obterá a saída abaixo:
Query OK, 1 row affected (0.08 sec) ...
-
Confirme os registros executando o comando abaixo:
SELECT * FROM stores;
Saída:
+----------+--------------+ | store_id | store_name | +----------+--------------+ | 1 | Philadelphia | | 2 | Galloway | +----------+--------------+ 2 rows in set (0.01 sec)
-
Em seguida, crie osproducts
tabela. A mesa conterá diferentes produtos oferecidos na loja:
CREATE TABLE products ( product_id BIGINT PRIMARY KEY AUTO_INCREMENT, product_name VARCHAR(40), cost_price DOUBLE, retail_price DOUBLE, availability VARCHAR(5) ) ENGINE=InnoDB;
Saída:
Query OK, 0 rows affected (0.13 sec)
-
Cada produto será identificado exclusivamente por umproduct_id
.
-
Umproduct_name
campo irá especificar os nomes dos itens.
-
Ocost_price
eretail_price
campos determinarão o preço de compra e venda, respectivamente.
-
Umaavailability
coluna definirá a disponibilidade do produto nas diferentes lojas. Se o produto estiver disponível apenas em nossa loja local (Filadélfia), nós o denotaremos com umLOCAL
valor. Caso contrário, usaremos o valor deALL
para significar um produto que está disponível em ambas as lojas (Filadélfia e Galloway).
-
-
Adicione dados de amostra aosproducts
tabela:
INSERT INTO products (product_name, cost_price, retail_price, availability) VALUES ('WIRELESS MOUSE', '18.23', '30.25','ALL'); INSERT INTO products (product_name, cost_price, retail_price, availability) VALUES ('8 MP CAMERA', '60.40', '85.40','ALL'); INSERT INTO products (product_name, cost_price, retail_price, availability) VALUES ('SMART WATCH', '189.60', '225.30','LOCAL');
Você obterá a saída mostrada abaixo após cada comando de inserção:
Query OK, 1 row affected (0.02 sec) ...
-
Confirme se os produtos foram inseridos executando o comando abaixo:
SELECT * FROM products;
Saída:
+------------+----------------+------------+--------------+--------------+ | product_id | product_name | cost_price | retail_price | availability | +------------+----------------+------------+--------------+--------------+ | 1 | WIRELESS MOUSE | 18.23 | 30.25 | ALL | | 2 | 8 MP CAMERA | 60.4 | 85.4 | ALL | | 3 | SMART WATCH | 189.6 | 225.3 | LOCAL | +------------+----------------+------------+--------------+--------------+ 3 rows in set (0.00 sec)
-
Em seguida, a disponibilidade dos produtos será mapeada para outra tabela chamadaproducts_to_stores
. Esta tabela fará referência apenas aoproduct_id
dosproducts
tabela e ostore_id
dasstores
tabela onde o item está disponível.
Crie osproducts_to_stores
tabela executando o código abaixo:
CREATE TABLE products_to_stores ( ref_id BIGINT PRIMARY KEY AUTO_INCREMENT, product_id BIGINT, store_id BIGINT ) ENGINE=InnoDB;
Saída:
Query OK, 0 rows affected (0.14 sec)
-
Em seguida, criaremos umarchived_products
tabela. A tabela conterá informações sobre produtos excluídos para referência futura:
CREATE TABLE archived_products ( product_id BIGINT PRIMARY KEY , product_name VARCHAR(40), cost_price DOUBLE, retail_price DOUBLE, availability VARCHAR(5) ) ENGINE=InnoDB;
Saída:
Query OK, 0 rows affected (0.14 sec)
-
Por fim, criaremos umproducts_price_history
tabela para acompanhar os diferentes preços de cada produto ao longo do tempo:
CREATE TABLE products_price_history ( product_id BIGINT PRIMARY KEY AUTO_INCREMENT, price_date DATETIME, retail_price DOUBLE ) ENGINE=InnoDB;
Saída:
Query OK, 0 rows affected (0.14 sec)
Uma vez que nossa estrutura de banco de dados esteja pronta, podemos prosseguir e aprender a sintaxe básica de um gatilho de banco de dados MySQL para criar nossa primeira amostra.
Sintaxe do gatilho
Conforme indicado anteriormente, os gatilhos são disparados automaticamente antes ou depois que um comando SQL é executado no banco de dados. A sintaxe básica para criar acionadores é a seguinte:
CREATE TRIGGER TRIGGER_NAME
TRIGGER_TIME TRIGGER_EVENT
ON TABLE_NAME FOR EACH ROW
[TRIGGER BODY];
-
TRIGGER_NAME
:Cada acionador deve ter um nome exclusivo e você deve defini-lo aqui.
-
TRIGGER_TIME
:OuBEFORE
ouAFTER
.
-
TRIGGER_EVENT
:Você precisa especificar o evento do banco de dados que invocará o gatilho:INSERT
,UPDATE
, ouDELETE
.
-
TRIGGER BODY
:especifica o comando SQL real (ou comandos) que você deseja que seja executado pelo seu gatilho.
Se um corpo de gatilho tiver mais de uma instrução SQL, você deve colocá-lo dentro de um
BEGIN...END
quadra. Além disso, você precisará alterar temporariamente o DELIMITER
que sinaliza o fim do corpo do gatilho para um novo valor. Isso garante que as instruções dentro do corpo não sejam interpretadas prematuramente pelo seu cliente MySQL. Um exemplo disso se parece com o seguinte:DELIMITER &&
CREATE TRIGGER TRIGGER_NAME
TRIGGER_TIME TRIGGER_EVENT
ON TABLE_NAME FOR EACH ROW
BEGIN
[TRIGGER BODY]
END &&
DELIMITER ;
Observação A última linha deste exemplo altera oDELIMITER
de volta ao padrão;
valor.
Criando antes dos acionadores de eventos
Nesta seção, examinaremos os diferentes tipos de gatilhos que são disparados antes de uma operação de banco de dados. Estes incluem o
BEFORE INSERT
, BEFORE UPDATE
e BEFORE DELETE
gatilhos. Criando um gatilho antes da inserção
Vamos criar nosso primeiro
BEFORE INSERT
acionar. O acionador garantirá que o preço de varejo de um produto seja maior que o preço de custo sempre que os itens forem inseridos nos products
tabela. Caso contrário, o usuário do banco de dados receberá um erro. -
Ainda nomysql >
prompt, digite o comando abaixo:
DELIMITER $$ CREATE TRIGGER price_validator BEFORE INSERT ON products FOR EACH ROW IF NEW.cost_price>=NEW.retail_price THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Retail price must be greater than cost price.'; END IF $$ DELIMITER ;
-
O código acima define o nome do gatilho (price_validator
), hora (BEFORE
), evento (INSERT
) e a tabela (products
) a ser afetado.
-
Nosso gatilho usa oNEW
palavra-chave para verificar ocost_price
eretail_price
antes de um registro ser inserido nosproducts
tabela, usando oIF...THEN...END IF
demonstração.
-
Se ocost_price
é maior ou igual aoretail price
, nossos gatilhos dizem ao MySQL para lançar uma exceção personalizada instruindo o usuário a corrigir o erro.
-
-
Para testar o gatilho acima, tente inserir um produto que viole a regra de validação:
INSERT INTO products (product_name, cost_price, retail_price, availability) VALUES ('GAMING MOUSE PAD', '145.00', '144.00','LOCAL');
Saída:
ERROR 1644 (45000): Retail price must be greater than cost price.
Os comandos de inserção acima devem falhar porque oretail_price
(144,00) não é maior que ocost_price
(145,00).
Criando um gatilho antes da atualização
Em seguida, criaremos um
BEFORE UPDATE
acionar. Esse acionador impedirá que os usuários do banco de dados editem um nome de produto depois que um produto for inserido no banco de dados. Se você tiver vários usuários trabalhando no banco de dados, um BEFORE UPDATE
trigger pode ser usado para tornar os valores somente leitura, e isso pode impedir que usuários mal-intencionados ou descuidados modifiquem registros desnecessariamente. -
Crie um novoproduct_name_validator
trigger com o comando abaixo:
DELIMITER $$ CREATE TRIGGER product_name_validator BEFORE UPDATE ON products FOR EACH ROW IF NEW.product_name<>OLD.product_name THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Product name is read-only and it can not be changed.'; END IF $$ DELIMITER ;
Este acionador compara os valores do novoproduct_name
(NEW.product_name
) e o nome antigo já no banco de dados (OLD.product_name
). Se houver uma incompatibilidade, uma exceção será lançada.
-
Para invocar oproduct_name_validator
acionador, podemos tentar atualizar o nome do produto com o ID1
:
UPDATE products SET product_name='WIRELESS BLUETOOTH MOUSE' WHERE product_id='1';
Saída:
ERROR 1644 (45000): Product name is read-only and it can not be changed.
Definindo um gatilho antes da exclusão
Nesta seção, você verá como definir um
BEFORE DELETE
gatilho para impedir que os usuários excluam registros específicos de uma tabela. -
Para criar oprevent_delete
trigger, execute o comando abaixo:
DELIMITER $$ CREATE TRIGGER prevent_delete BEFORE DELETE ON products FOR EACH ROW IF OLD.availability='ALL' THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'The product can not be deleted because it is available in ALL stores.'; END IF $$ DELIMITER ;
Este acionador impedirá produtos marcados com um valor deALL
na coluna de disponibilidade de ser excluído.
-
Em seguida, tente excluir o primeiro produto da tabela de produtos e veja se o gatilho será invocado:
DELETE FROM products WHERE product_id='1';
Saída:
ERROR 1644 (45000): The product can not be deleted because it is available in ALL stores.
Examinamos os diferentes gatilhos que são invocados antes de uma operação de banco de dados. Em seguida, examinaremos os outros tipos de gatilhos que são disparados após eventos do banco de dados.
Criando acionadores pós-evento
Em um ambiente de produção, você pode querer que alguns gatilhos sejam executados automaticamente após a ocorrência de um evento de banco de dados (por exemplo, inserir registros em tabelas diferentes). Os exemplos abaixo demonstram como esses tipos de gatilhos podem ser usados em nosso banco de dados de exemplo.
Criando um gatilho após inserção
Este exemplo cria um gatilho chamado
product_availability
que insere registros de mapeamento no products_to_stores
tabela. Esse gatilho é usado para impor a lógica de negócios; em particular, ajuda a definir a disponibilidade do produto para as diferentes lojas. -
Execute o código abaixo para criar oproduct_availability
acionar. Como temos várias linhas de código no corpo do gatilho, usaremos umBEGIN...END
quadra:
DELIMITER $$ CREATE TRIGGER product_availability AFTER INSERT ON products FOR EACH ROW BEGIN IF NEW.availability='LOCAL' then INSERT INTO products_to_stores (product_id, store_id) VALUES (NEW.product_id, '1'); ELSE INSERT INTO products_to_stores (product_id, store_id) VALUES (NEW.product_id, '1'); INSERT INTO products_to_stores (product_id, store_id) VALUES (NEW.product_id, '2'); END IF; END $$ DELIMITER ;
-
Quando um item está sendo inserido nosproducts
tabela, o gatilho verificará aavailability
campo.
-
Se estiver marcado com oLOCAL
valor, o produto será disponibilizado em apenas uma loja.
-
Qualquer outro valor instruirá o gatilho a disponibilizar o produto para as duas lojas que criamos anteriormente.
-
-
Para ver aproduct_availability
trigger em ação, insira os dois registros na tabela de produtos:
INSERT INTO products (product_name, cost_price, retail_price, availability) VALUES ('BLUETOOTH KEYBOARD', '17.60', '23.30','LOCAL'); INSERT INTO products (product_name, cost_price, retail_price, availability) VALUES ('DVB-T2 RECEIVE', '49.80', '53.40','ALL');
-
Em seguida, consulte osproducts_to_stores
tabela:
SELECT * FROM products_to_stores;
Você deverá ver uma saída semelhante à mostrada abaixo:
+--------+------------+----------+ | ref_id | product_id | store_id | +--------+------------+----------+ | 1 | 4 | 1 | | 2 | 5 | 1 | | 3 | 5 | 2 | +--------+------------+----------+ 3 rows in set (0.00 sec)
Definindo um gatilho após a atualização
Um gatilho também pode ser disparado após um
UPDATE
evento. Veremos como podemos aproveitar esse tipo de gatilho para acompanhar as mudanças de preços em nossa loja ao longo do tempo. -
Crie umproduct_history_updater
trigger executando o comando abaixo:
CREATE TRIGGER product_history_updater AFTER UPDATE ON products FOR EACH ROW INSERT INTO products_price_history (product_id, price_date, retail_price) VALUES (OLD.product_id, NOW(), NEW.retail_price);
Esse acionador registra alterações noretail_price
de um produto noproducts_price_history
tabela.
Observação Ao contrário dos exemplos anteriores, este gatilho possui apenas uma instrução no corpo do gatilho, portanto, não precisamos alterar o
DELIMITER
. -
Em seguida, tente atualizar o preço do primeiro produto executando o comando abaixo:
UPDATE products SET retail_price='36.75' WHERE product_id='1';
-
Em seguida, consulte oproducts_price_history
tabela para ver se a alteração de preço foi registrada:
SELECT * FROM products_price_history;
Se o gatilho funcionou conforme o esperado, você deve obter a saída abaixo:
+------------+---------------------+--------------+ | product_id | price_date | retail_price | +------------+---------------------+--------------+ | 1 | 2020-01-28 11:46:21 | 36.75 | +------------+---------------------+--------------+ 1 row in set (0.00 sec)
Criando um gatilho após a exclusão
Em alguns casos, você pode querer registrar operações de exclusão após uma ação específica ter ocorrido no banco de dados. Você pode conseguir isso usando o
AFTER DELETE
acionar. -
Crie um novoproduct_archiver
trigger com o comando abaixo:
CREATE TRIGGER product_archiver AFTER DELETE ON products FOR EACH ROW INSERT INTO archived_products (product_id, product_name, cost_price, retail_price, availability) VALUES (OLD.product_id, OLD.product_name, OLD.cost_price, OLD.retail_price, OLD.availability);
Este acionador arquiva os produtos excluídos em uma tabela separada chamadaarchived_products
. Quando um item é excluído dos principaisproducts
tabela, nosso gatilho irá registrá-lo automaticamente noarchived_products
tabela para referência futura.
-
Em seguida, exclua um produto dosproducts
table e veja se o trigger será invocado:
DELETE FROM products WHERE product_id='3';
-
Agora, se você verificar osarchived_products
tabela, você deve ver um registro:
SELECT * FROM archived_products;
Saída:
+------------+--------------+------------+--------------+--------------+ | product_id | product_name | cost_price | retail_price | availability | +------------+--------------+------------+--------------+--------------+ | 3 | SMART WATCH | 189.6 | 225.3 | LOCAL | +------------+--------------+------------+--------------+--------------+ 1 row in set (0.00 sec)
Excluindo um gatilho
Você viu os diferentes tipos de gatilhos e como eles podem ser usados em um ambiente de produção. Às vezes, você pode querer remover um gatilho do banco de dados.
Você pode excluir um gatilho se não quiser mais usá-lo usando a sintaxe abaixo:
DROP TRIGGER IF EXISTS TRIGGER_NAME;
Observação O IF EXISTS
palavra-chave é um parâmetro opcional que só exclui um gatilho se ele existir.
Por exemplo, para excluir o arquivo
product_archiving
trigger que definimos acima, use o comando abaixo:DROP TRIGGER IF EXISTS product_archiver;
Saída:
Query OK, 0 rows affected (0.00 sec)
Cuidado Seja cauteloso ao excluir tabelas associadas a gatilhos. Depois que uma tabela é removida do banco de dados MySQL, os gatilhos relacionados também são excluídos automaticamente.
Mais informações
Você pode querer consultar os seguintes recursos para obter informações adicionais sobre este tópico. Embora estes sejam fornecidos na esperança de que sejam úteis, observe que não podemos garantir a precisão ou pontualidade dos materiais hospedados externamente.
- Sintaxe e exemplos do acionador do MySQL