sql >> Database >  >> RDS >> Mysql

Werken met triggers in een MySQL-database - een zelfstudie

Een trigger is een vooraf gedefinieerd SQL-commando dat automatisch wordt uitgevoerd wanneer specifieke acties in de database plaatsvinden. Het kan worden afgevuurd voor of na een INSERT , UPDATE , of DELETE evenement.

Triggers worden voornamelijk gebruikt om softwarelogica in de MySQL-server te behouden en ze hebben verschillende voordelen:

  • Triggers helpen de wereldwijde activiteiten op één locatie te centraliseren.

  • Ze verminderen de code aan de clientzijde en helpen de retourvluchten naar de databaseserver te minimaliseren.

  • Ze helpen applicaties schaalbaarder te maken op verschillende platforms.

Enkele veelvoorkomende use-cases van triggers zijn onder meer het loggen van audits, het vooraf berekenen van databasewaarden (bijv. cumulatieve sommen) en het afdwingen van complexe regels voor gegevensintegriteit en validatie.

In deze handleiding leert u:

  • Hoe de syntaxis voor een trigger is gestructureerd.

  • Hoe triggers te maken die worden uitgevoerd voordat andere databasegebeurtenissen plaatsvinden.

  • Hoe triggers te maken die worden uitgevoerd nadat andere databasegebeurtenissen hebben plaatsgevonden.

  • Hoe triggers te verwijderen.

Voordat u begint

  1. Als je dit nog niet hebt gedaan, maak dan een Linode-account en Compute Instance aan. Zie onze handleidingen Aan de slag met Linode en Een rekeninstantie maken.

  2. Volg onze handleiding voor het instellen en beveiligen van een rekeninstantie om uw systeem bij te werken. Mogelijk wilt u ook de tijdzone instellen, uw hostnaam configureren, een beperkt gebruikersaccount maken en SSH-toegang versterken.

  3. Een MySQL-server en client geïnstalleerd op de Linode-server. Installatiehandleidingen voor MySQL zijn beschikbaar voor verschillende distributies in onze MySQL-sectie.

Bereid de database voor

Om beter te begrijpen hoe triggers werken, zullen we een voorbeelddatabase maken en hieraan voorbeeldgegevens toevoegen. Later zullen we verschillende triggers in de database maken als een proof of concept-oefening.

  1. Log eerst in op uw MySQL-server:

    mysql -u root -p
    

    Voer vervolgens het root-wachtwoord van uw MySQL-server in en druk op Enter om door te gaan.

  2. Vervolgens ziet u een MySQL-prompt die lijkt op de onderstaande:

    mysql >
  3. Maak een test_database door het onderstaande commando uit te voeren:

    CREATE DATABASE test_database;
    

    Uitgang:

    Query OK, 1 row affected (0.02 sec)
  4. Schakel over naar de database:

    USE test_database;
    

    Uitgang:

    Database changed
  5. Zodra de database is geselecteerd, zullen we enkele tabellen maken die we zullen gebruiken om triggers te demonstreren. We beginnen met het maken van de stores tafel. Deze tabel bevat informatie over twee voorbeeldwinkels/kantoren van waaruit ons hypothetische bedrijf opereert:

    CREATE TABLE stores
    (
    store_id BIGINT PRIMARY KEY AUTO_INCREMENT,
    store_name VARCHAR(50)
    ) ENGINE=InnoDB;
    

    Uitgang:

    Query OK, 0 rows affected (0.07 sec)
  6. Voeg vervolgens twee records toe aan de stores tabel door de onderstaande commando's uit te voeren:

    INSERT INTO stores (store_name) VALUES ('Philadelphia');
    INSERT INTO stores (store_name) VALUES ('Galloway');
    

    Na elke opdracht krijgt u de onderstaande uitvoer:

    Query OK, 1 row affected (0.08 sec)
    ...
  7. Bevestig de records door de onderstaande opdracht uit te voeren:

    SELECT * FROM stores;
    

    Uitgang:

    +----------+--------------+
    | store_id | store_name   |
    +----------+--------------+
    |        1 | Philadelphia |
    |        2 | Galloway     |
    +----------+--------------+
    2 rows in set (0.01 sec)
  8. Maak vervolgens de products tafel. De tafel zal verschillende producten bevatten die in de winkel worden aangeboden:

    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;
    

    Uitgang:

    Query OK, 0 rows affected (0.13 sec)
    • Elk product wordt uniek geïdentificeerd door een product_id .

    • Een product_name veld specificeert de namen van de items.

    • De cost_price en retail_price velden bepalen respectievelijk de koop- en verkoopprijs.

    • Een availability kolom bepaalt de productbeschikbaarheid in de verschillende winkels. Als het product alleen verkrijgbaar is in onze lokale winkel (Philadelphia), geven we het aan met een LOCAL waarde. Anders gebruiken we de waarde van ALL om een ​​product aan te duiden dat in beide winkels (Philadelphia en Galloway) verkrijgbaar is.

  9. Voeg voorbeeldgegevens toe aan de products tafel:

    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');
    

    U krijgt de onderstaande uitvoer na elk invoegcommando:

    Query OK, 1 row affected (0.02 sec)
    ...
  10. Bevestig of de producten zijn ingevoegd door de onderstaande opdracht uit te voeren:

    SELECT * FROM products;
    

    Uitgang:

    +------------+----------------+------------+--------------+--------------+
    | 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. Vervolgens wordt de beschikbaarheid van de producten toegewezen aan een andere tabel met de naam products_to_stores . Deze tabel verwijst alleen naar de product_id van de products tabel en de store_id uit de stores tabel waar het item beschikbaar is.

    Maak de products_to_stores tabel door de onderstaande code uit te voeren:

    CREATE TABLE products_to_stores
    (
    ref_id BIGINT PRIMARY KEY AUTO_INCREMENT,
    product_id BIGINT,
    store_id BIGINT
    ) ENGINE=InnoDB;
    

    Uitgang:

    Query OK, 0 rows affected (0.14 sec)
  12. Vervolgens maken we een archived_products tafel. De tabel bevat informatie over verwijderde producten voor toekomstig gebruik:

    CREATE TABLE archived_products
    (
    product_id BIGINT PRIMARY KEY ,
    product_name VARCHAR(40),
    cost_price DOUBLE,
    retail_price DOUBLE,
    availability VARCHAR(5)
    ) ENGINE=InnoDB;
    

    Uitgang:

    Query OK, 0 rows affected (0.14 sec)
  13. Ten slotte maken we een products_price_history tabel voor het volgen van de verschillende prijzen van elk product in de loop van de tijd:

    CREATE TABLE products_price_history
    (
    product_id BIGINT PRIMARY KEY AUTO_INCREMENT,
    price_date DATETIME,
    retail_price DOUBLE
    ) ENGINE=InnoDB;
    

    Uitgang:

    Query OK, 0 rows affected (0.14 sec)

Zodra onze databasestructuur op zijn plaats is, kunnen we nu doorgaan en de basissyntaxis van een MySQL-databasetrigger leren om ons eerste voorbeeld te maken.

Triggersyntaxis

Zoals eerder aangegeven, worden triggers automatisch geactiveerd voordat of nadat een SQL-opdracht in de database is uitgevoerd. De basissyntaxis voor het maken van triggers is als volgt:

CREATE TRIGGER TRIGGER_NAME

TRIGGER_TIME TRIGGER_EVENT

ON TABLE_NAME FOR EACH ROW

[TRIGGER BODY];
  • TRIGGER_NAME :Elke trigger moet een unieke naam hebben en u moet deze hier definiëren.

  • TRIGGER_TIME :Ofwel BEFORE of AFTER .

  • TRIGGER_EVENT :U moet de databasegebeurtenis specificeren die de trigger zal aanroepen:INSERT , UPDATE , of DELETE .

  • TRIGGER BODY :Dit specificeert de daadwerkelijke SQL-opdracht (of opdrachten) die u wilt laten uitvoeren door uw trigger.

Als een triggerbody meer dan één SQL-instructie heeft, moet u deze insluiten in een BEGIN...END blok. U moet ook tijdelijk de DELIMITER . wijzigen die het einde van het triggerlichaam naar een nieuwe waarde signaleert. Dit zorgt ervoor dat de statements in de body niet voortijdig worden geïnterpreteerd door uw MySQL-client. Een voorbeeld hiervan ziet er als volgt uit:

DELIMITER &&

CREATE TRIGGER TRIGGER_NAME

TRIGGER_TIME TRIGGER_EVENT

ON TABLE_NAME FOR EACH ROW

BEGIN

[TRIGGER BODY]

END &&

DELIMITER ;
Opmerking De laatste regel van dit voorbeeld verandert de DELIMITER terug naar de standaard ; waarde.

Creëren voor gebeurtenistriggers

In deze sectie zullen we kijken naar de verschillende soorten triggers die worden geactiveerd voordat een databasebewerking wordt uitgevoerd. Deze omvatten de BEFORE INSERT , BEFORE UPDATE , en BEFORE DELETE triggers.

Een trigger voor invoegen maken

We zullen onze eerste BEFORE INSERT . maken trekker. De trigger zorgt ervoor dat de verkoopprijs van een product hoger is dan de kostprijs wanneer artikelen worden ingevoegd in de products tafel. Anders krijgt de databasegebruiker een foutmelding.

  1. Terwijl je nog op de mysql > prompt, voer de onderstaande opdracht in:

    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 ;
    
    • De bovenstaande code definieert de triggernaam (price_validator ), tijd (BEFORE ), gebeurtenis (INSERT ), en de tabel (products ) worden beïnvloed.

    • Onze trigger gebruikt de NEW trefwoord om de cost_price . te controleren en retail_price voordat een record wordt ingevoegd in de products tabel, met behulp van de IF...THEN...END IF verklaring.

    • Als de cost_price is groter dan of gelijk aan de retail price , onze triggers vertellen MySQL om een ​​aangepaste uitzondering te genereren die de gebruiker opdraagt ​​de fout te herstellen.

  2. Om de bovenstaande trigger te testen, probeert u een product in te voegen dat de validatieregel schendt:

    INSERT INTO products (product_name, cost_price, retail_price, availability) VALUES ('GAMING MOUSE PAD', '145.00', '144.00','LOCAL');
    

    Uitgang:

    ERROR 1644 (45000): Retail price must be greater than cost price.

    De bovenstaande invoegopdrachten zouden moeten mislukken omdat de retail_price (144.00) is niet groter dan de cost_price (145,00).

Een trigger vóór update maken

Vervolgens maken we een BEFORE UPDATE trekker. Deze trigger voorkomt dat databasegebruikers een productnaam kunnen bewerken nadat een product in de database is ingevoegd. Als er meerdere gebruikers in de database werken, verschijnt een BEFORE UPDATE trigger kan worden gebruikt om waarden alleen-lezen te maken, en dit kan voorkomen dat kwaadwillende of onzorgvuldige gebruikers records onnodig wijzigen.

  1. Maak een nieuwe product_name_validator trigger met het onderstaande commando:

    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 ;
    

    Deze trigger vergelijkt de waarden van de nieuwe product_name (NEW.product_name ) en de oude naam die al in de database staat (OLD.product_name ). Als er een mismatch is, wordt er een uitzondering gegenereerd.

  2. Om de product_name_validator aan te roepen trigger, kunnen we proberen de naam van het product bij te werken met de ID 1 :

    UPDATE products SET product_name='WIRELESS BLUETOOTH MOUSE' WHERE product_id='1';
    

    Uitgang:

    ERROR 1644 (45000): Product name is read-only and it can not be changed.

Een trigger voor verwijderen definiëren

In dit gedeelte ziet u hoe u een BEFORE DELETE . kunt definiëren trigger om te voorkomen dat gebruikers specifieke records uit een tabel verwijderen.

  1. Om de prevent_delete te maken trigger, voer de onderstaande opdracht uit:

    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 ;
    

    Deze trigger voorkomt dat producten gemarkeerd met de waarde ALL in de beschikbaarheidskolom om te worden verwijderd.

  2. Probeer vervolgens het eerste product uit de producttabel te verwijderen en kijk of de trigger wordt aangeroepen:

    DELETE FROM products WHERE product_id='1';
    

    Uitgang:

    ERROR 1644 (45000): The product can not be deleted because it is available in ALL stores.

We hebben gekeken naar de verschillende triggers die worden aangeroepen vóór een databasebewerking. Vervolgens zullen we kijken naar de andere soorten triggers die worden geactiveerd na databasegebeurtenissen.

Creëren van after-event-triggers

In een productieomgeving wilt u misschien dat sommige triggers automatisch worden uitgevoerd nadat een databasegebeurtenis heeft plaatsgevonden (bijvoorbeeld door records in verschillende tabellen in te voegen). De onderstaande voorbeelden laten zien hoe dit soort triggers kunnen worden gebruikt in onze voorbeelddatabase.

Een After Insert-trigger maken

In dit voorbeeld wordt een trigger gemaakt met de naam product_availability die mapping records invoegt in de products_to_stores tafel. Deze trigger wordt gebruikt om bedrijfslogica af te dwingen; in het bijzonder helpt het bij het bepalen van de productbeschikbaarheid voor de verschillende winkels.

  1. Voer de onderstaande code uit om de product_availability . te maken trekker. Omdat we meerdere regels code in de hoofdtekst van de trigger hebben, gebruiken we een BEGIN...END blok:

    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 ;
    
    • Wanneer een artikel wordt ingevoegd in de products tabel, de trigger controleert de availability veld.

    • Als het is gemarkeerd met de LOCAL waarde, wordt het product slechts in één winkel beschikbaar gesteld.

    • Elke andere waarde zal de trigger instrueren om het product beschikbaar te maken voor de twee winkels die we eerder hebben gemaakt.

  2. Om de product_availability te zien trigger in actie, voeg de twee records toe aan de producttabel:

    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. Vraag vervolgens de products_to_stores tafel:

    SELECT * FROM products_to_stores;
    

    U zou een uitvoer moeten zien die lijkt op die hieronder:

    +--------+------------+----------+
    | ref_id | product_id | store_id |
    +--------+------------+----------+
    |      1 |          4 |        1 |
    |      2 |          5 |        1 |
    |      3 |          5 |        2 |
    +--------+------------+----------+
    3 rows in set (0.00 sec)

Een after-update-trigger definiëren

Een trigger kan ook worden geactiveerd na een UPDATE evenement. We zullen zien hoe we dit type trigger kunnen gebruiken om prijsveranderingen in onze winkel in de loop van de tijd bij te houden.

  1. Maak een product_history_updater trigger door de onderstaande opdracht uit te voeren:

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

    Deze trigger registreert wijzigingen in de retail_price van een product in de products_price_history tafel.

    Opmerking In tegenstelling tot eerdere voorbeelden heeft deze trigger slechts één instructie in de hoofdtekst van de trigger, dus we hoeven de DELIMITER niet te wijzigen .
  2. Probeer vervolgens de prijs van het eerste product bij te werken door de onderstaande opdracht uit te voeren:

    UPDATE products SET retail_price='36.75' WHERE product_id='1';
    
  3. Vraag vervolgens de products_price_history tabel om te zien of de prijswijziging is vastgelegd:

    SELECT * FROM products_price_history;
    

    Als de trigger werkte zoals verwacht, zou je de onderstaande output moeten krijgen:

    +------------+---------------------+--------------+
    | product_id | price_date          | retail_price |
    +------------+---------------------+--------------+
    |          1 | 2020-01-28 11:46:21 |        36.75 |
    +------------+---------------------+--------------+
    1 row in set (0.00 sec)

Een trigger voor na het verwijderen maken

In sommige gevallen wilt u wellicht verwijderingsbewerkingen vastleggen nadat een specifieke actie in de database heeft plaatsgevonden. U kunt dit bereiken door de AFTER DELETE . te gebruiken trigger.

  1. Maak een nieuwe product_archiver trigger met het onderstaande commando:

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

    Deze trigger archiveert verwijderde producten in een aparte tabel met de naam archived_products . Wanneer een item wordt verwijderd uit de belangrijkste products tabel, zal onze trigger deze automatisch loggen in de archived_products tabel voor toekomstig gebruik.

  2. Verwijder vervolgens een product uit de products tabel en kijk of de trigger wordt aangeroepen:

    DELETE FROM products WHERE product_id='3';
    
  3. Als u nu de archived_products . aanvinkt tabel, zou u één record moeten zien:

    SELECT * FROM archived_products;
    

    Uitgang:

    +------------+--------------+------------+--------------+--------------+
    | product_id | product_name | cost_price | retail_price | availability |
    +------------+--------------+------------+--------------+--------------+
    |          3 | SMART WATCH  |      189.6 |        225.3 | LOCAL        |
    +------------+--------------+------------+--------------+--------------+
    1 row in set (0.00 sec)

Een trigger verwijderen

Je hebt de verschillende soorten triggers gezien en hoe ze in een productieomgeving kunnen worden gebruikt. Soms wil je misschien een trigger uit de database verwijderen.

U kunt een trigger verwijderen als u deze niet meer wilt gebruiken met behulp van de onderstaande syntaxis:

DROP TRIGGER IF EXISTS TRIGGER_NAME;
Opmerking De IF EXISTS trefwoord is een optionele parameter die alleen een trigger verwijdert als deze bestaat.

Om bijvoorbeeld de product_archiving . te verwijderen trigger die we hierboven hebben gedefinieerd, gebruik het onderstaande commando:

DROP TRIGGER IF EXISTS product_archiver;

Uitgang:

Query OK, 0 rows affected (0.00 sec)
Let op Wees voorzichtig bij het verwijderen van tabellen die zijn gekoppeld aan triggers. Zodra een tabel uit de MySQL-database is verwijderd, worden de gerelateerde triggers ook automatisch verwijderd.

Meer informatie

U kunt de volgende bronnen raadplegen voor aanvullende informatie over dit onderwerp. Hoewel deze worden verstrekt in de hoop dat ze nuttig zullen zijn, houd er rekening mee dat we niet kunnen instaan ​​voor de nauwkeurigheid of tijdigheid van extern gehost materiaal.

  • MySQL-triggersyntaxis en voorbeelden

  1. Een subset van kolommen selecteren uit de resultatenset van een opgeslagen procedure (T-SQL)

  2. MySQL wiskundige functies (volledige lijst)

  3. Hoe voer ik grote niet-blokkerende updates uit in PostgreSQL?

  4. PostgreSQL - max aantal parameters in IN-clausule?