Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Trabalhando com gatilhos em um banco de dados MySQL - um tutorial


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


  1. 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.

  2. 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.

  3. 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.

  1. 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.

  2. Em seguida, você verá um prompt do MySQL semelhante ao mostrado abaixo:
    mysql >

  3. Crie um test_database executando o comando abaixo:
    CREATE DATABASE test_database;
    

    Saída:
    Query OK, 1 row affected (0.02 sec)

  4. Mude para o banco de dados:
    USE test_database;
    

    Saída:
    Database changed

  5. Uma vez selecionado o banco de dados, criaremos algumas tabelas que usaremos para demonstrar as triggers. Começaremos criando as stores 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)

  6. Em seguida, adicione dois registros às stores 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)
    ...

  7. 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)

  8. Em seguida, crie os products 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 um product_id .

    • Um product_name campo irá especificar os nomes dos itens.

    • O cost_price e retail_price campos determinarão o preço de compra e venda, respectivamente.

    • Uma availability 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 um LOCAL valor. Caso contrário, usaremos o valor de ALL para significar um produto que está disponível em ambas as lojas (Filadélfia e Galloway).

  9. Adicione dados de amostra aos products 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)
    ...

  10. 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)

  11. Em seguida, a disponibilidade dos produtos será mapeada para outra tabela chamada products_to_stores . Esta tabela fará referência apenas ao product_id dos products tabela e o store_id das stores tabela onde o item está disponível.

    Crie os products_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)

  12. Em seguida, criaremos um archived_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)

  13. Por fim, criaremos um products_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 :Ou BEFORE ou AFTER .

  • TRIGGER_EVENT :Você precisa especificar o evento do banco de dados que invocará o gatilho:INSERT , UPDATE , ou DELETE .

  • 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 o DELIMITER 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.

  1. Ainda no mysql > 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 o NEW palavra-chave para verificar o cost_price e retail_price antes de um registro ser inserido nos products tabela, usando o IF...THEN...END IF demonstração.

    • Se o cost_price é maior ou igual ao retail price , nossos gatilhos dizem ao MySQL para lançar uma exceção personalizada instruindo o usuário a corrigir o erro.

  2. 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 o retail_price (144,00) não é maior que o cost_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.

  1. Crie um novo product_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 novo product_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.

  2. Para invocar o product_name_validator acionador, podemos tentar atualizar o nome do produto com o ID 1 :
    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.

  1. Para criar o prevent_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 de ALL na coluna de disponibilidade de ser excluído.

  2. 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.

  1. Execute o código abaixo para criar o product_availability acionar. Como temos várias linhas de código no corpo do gatilho, usaremos um BEGIN...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 nos products tabela, o gatilho verificará a availability campo.

    • Se estiver marcado com o LOCAL 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.

  2. Para ver a product_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');
    

  3. Em seguida, consulte os products_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.

  1. Crie um product_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 no retail_price de um produto no products_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 .

  2. 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';
    

  3. Em seguida, consulte o products_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.

  1. Crie um novo product_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 chamada archived_products . Quando um item é excluído dos principais products tabela, nosso gatilho irá registrá-lo automaticamente no archived_products tabela para referência futura.

  2. Em seguida, exclua um produto dos products table e veja se o trigger será invocado:
    DELETE FROM products WHERE product_id='3';
    

  3. Agora, se você verificar os archived_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