sql >> Databasteknik >  >> RDS >> Mysql

Arbeta med triggers i en MySQL-databas - en handledning

En utlösare är ett fördefinierat SQL-kommando som körs automatiskt när specifika åtgärder inträffar i databasen. Den kan aktiveras antingen före eller efter en INSERT , UPDATE , eller DELETE händelse.

Triggers används huvudsakligen för att upprätthålla mjukvarulogik i MySQL-servern, och de har flera fördelar:

  • Utlösare hjälper till att hålla den globala verksamheten centraliserad på en plats.

  • De minskar koden på klientsidan och hjälper till att minimera de rundresor som görs till databasservern.

  • De hjälper till att göra applikationer mer skalbara över olika plattformar.

Några vanliga användningsfall av utlösare inkluderar granskningsloggning, förberäkning av databasvärden (t.ex. kumulativa summor) och upprätthållande av komplexa dataintegritets- och valideringsregler.

I den här guiden kommer du att lära dig:

  • Hur syntaxen för en trigger är uppbyggd.

  • Hur man skapar triggers som exekveras innan andra databashändelser inträffar.

  • Hur man skapar triggers som exekveras efter att andra databashändelser inträffat.

  • Hur man tar bort triggers.

Innan du börjar

  1. Om du inte redan har gjort det, skapa ett Linode-konto och Compute Instance. Se våra guider Komma igång med Linode och Skapa en beräkningsinstans.

  2. Följ vår guide för att ställa in och säkra en beräkningsinstans för att uppdatera ditt system. Du kanske också vill ställa in tidszonen, konfigurera ditt värdnamn, skapa ett begränsat användarkonto och förstärka SSH-åtkomsten.

  3. En MySQL-server och klient installerad på Linode-servern. Installationsguider för MySQL finns tillgängliga för olika distributioner i vår MySQL-sektion.

Förbered databasen

För att bättre förstå hur utlösare fungerar kommer vi att skapa en exempeldatabas och lägga till exempeldata i den. Senare kommer vi att skapa olika triggers på databasen som en proof of concept-övning.

  1. Logga först in på din MySQL-server:

    mysql -u root -p
    

    Ange sedan root-lösenordet för din MySQL-server och tryck på Retur för att fortsätta.

  2. Därefter kommer du att se en MySQL-prompt som liknar den som visas nedan:

    mysql >
  3. Skapa en test_database genom att köra kommandot nedan:

    CREATE DATABASE test_database;
    

    Utdata:

    Query OK, 1 row affected (0.02 sec)
  4. Växla till databasen:

    USE test_database;
    

    Utdata:

    Database changed
  5. När databasen väl har valts kommer vi att skapa några tabeller som vi kommer att använda för att demonstrera triggers. Vi börjar med att skapa stores tabell. Den här tabellen innehåller information om två exempel på butiker/kontor där vår hypotetiska verksamhet verkar från:

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

    Utdata:

    Query OK, 0 rows affected (0.07 sec)
  6. Lägg sedan till två poster i stores tabell genom att köra kommandona nedan:

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

    Efter varje kommando får du följande utdata:

    Query OK, 1 row affected (0.08 sec)
    ...
  7. Bekräfta posterna genom att köra kommandot nedan:

    SELECT * FROM stores;
    

    Utdata:

    +----------+--------------+
    | store_id | store_name   |
    +----------+--------------+
    |        1 | Philadelphia |
    |        2 | Galloway     |
    +----------+--------------+
    2 rows in set (0.01 sec)
  8. Skapa sedan products tabell. Bordet kommer att innehålla olika produkter som erbjuds i butiken:

    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;
    

    Utdata:

    Query OK, 0 rows affected (0.13 sec)
    • Varje produkt kommer att identifieras unikt av ett product_id .

    • Ett product_name fältet kommer att ange namnen på objekten.

    • cost_price och retail_price fält kommer att bestämma köp- och försäljningspriset.

    • En availability kolumnen kommer att definiera produkttillgängligheten i de olika butikerna. Om produkten endast är tillgänglig i vår lokala butik (Philadelphia), kommer vi att beteckna den med en LOCAL värde. Annars kommer vi att använda värdet för ALL för att beteckna en produkt som är tillgänglig i båda butikerna (Philadelphia och Galloway).

  9. Lägg till exempeldata till products tabell:

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

    Du kommer att få utdata som visas nedan efter varje insert-kommando:

    Query OK, 1 row affected (0.02 sec)
    ...
  10. Bekräfta om produkterna har infogats genom att köra kommandot nedan:

    SELECT * FROM products;
    

    Utdata:

    +------------+----------------+------------+--------------+--------------+
    | 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. Därefter kommer produkternas tillgänglighet att mappas till en annan tabell med namnet products_to_stores . Den här tabellen refererar bara till product_id från products tabellen och store_id från stores tabell där artikeln är tillgänglig.

    Skapa products_to_stores genom att köra koden nedan:

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

    Utdata:

    Query OK, 0 rows affected (0.14 sec)
  12. Därefter kommer vi att skapa en archived_products tabell. Tabellen innehåller information om raderade produkter för framtida referens:

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

    Utdata:

    Query OK, 0 rows affected (0.14 sec)
  13. Slutligen kommer vi att skapa en products_price_history tabell för att spåra de olika priserna för varje produkt över tiden:

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

    Utdata:

    Query OK, 0 rows affected (0.14 sec)

När vår databasstruktur är på plats kan vi nu gå vidare och lära oss den grundläggande syntaxen för en MySQL-databasutlösare för att skapa vårt första exempel.

Triggersyntax

Som nämnts tidigare utlöses triggers automatiskt antingen före eller efter att ett SQL-kommando körs i databasen. Den grundläggande syntaxen för att skapa utlösare är följande:

CREATE TRIGGER TRIGGER_NAME

TRIGGER_TIME TRIGGER_EVENT

ON TABLE_NAME FOR EACH ROW

[TRIGGER BODY];
  • TRIGGER_NAME :Varje utlösare måste ha ett unikt namn och du bör definiera det här.

  • TRIGGER_TIME :Antingen BEFORE eller AFTER .

  • TRIGGER_EVENT :Du måste ange databashändelsen som kommer att anropa triggern:INSERT , UPDATE , eller DELETE .

  • TRIGGER BODY :Detta anger det faktiska SQL-kommandot (eller de kommandon) som du vill ska köras av din utlösare.

Om en utlösarkropp har mer än en SQL-sats måste du omsluta den i en BEGIN...END blockera. Dessutom måste du tillfälligt ändra DELIMITER som signalerar slutet av triggerkroppen till ett nytt värde. Detta säkerställer att påståendena i kroppen inte tolkas i förtid av din MySQL-klient. Ett exempel på detta ser ut som följande:

DELIMITER &&

CREATE TRIGGER TRIGGER_NAME

TRIGGER_TIME TRIGGER_EVENT

ON TABLE_NAME FOR EACH ROW

BEGIN

[TRIGGER BODY]

END &&

DELIMITER ;
Obs Den sista raden i detta exempel ändrar DELIMITER tillbaka till standard ; värde.

Skapa före händelseutlösare

I det här avsnittet kommer vi att titta på de olika typerna av utlösare som utlöses före en databasoperation. Dessa inkluderar koden BEFORE INSERT , BEFORE UPDATE och BEFORE DELETE utlösare.

Skapa en utlösare före infogning

Vi kommer att skapa vår första BEFORE INSERT utlösare. Utlösaren ser till att detaljpriset för en produkt är högre än självkostnadspriset när artiklar infogas i products tabell. Annars kommer databasanvändaren att få ett felmeddelande.

  1. Medan du fortfarande är på mysql > skriv kommandot nedan:

    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 ;
    
    • Ovanstående kod definierar triggernamnet (price_validator ), tid (BEFORE ), händelse (INSERT ), och tabellen (products ) att påverkas.

    • Vår trigger använder NEW sökord för att kontrollera cost_price och retail_price innan en post infogas i products tabell, med hjälp av IF...THEN...END IF uttalande.

    • Om cost_price är större eller lika med retail price , säger våra utlösare till MySQL att skapa ett anpassat undantag som instruerar användaren att rätta till felet.

  2. För att testa triggern ovan, försök att infoga en produkt som bryter mot valideringsregeln:

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

    Utdata:

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

    Ovanstående infogningskommandon bör misslyckas eftersom retail_price (144,00) är inte större än cost_price (145.00).

Skapa en utlösare före uppdatering

Därefter kommer vi att skapa en BEFORE UPDATE utlösare. Denna utlösare kommer att förhindra databasanvändare från att redigera ett produktnamn när en produkt har infogats i databasen. Om du har flera användare som arbetar i databasen, en BEFORE UPDATE trigger kan användas för att göra värden skrivskyddade, och detta kan förhindra illvilliga eller vårdslösa användare från att modifiera poster i onödan.

  1. Skapa en ny product_name_validator utlösa med kommandot nedan:

    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 ;
    

    Den här utlösaren jämför värdena för den nya product_name (NEW.product_name ) och det gamla namnet som redan finns i databasen (OLD.product_name ). Om det finns en missmatchning, kastas ett undantag.

  2. För att anropa product_name_validator trigger, kan vi försöka uppdatera namnet på produkten med ID 1 :

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

    Utdata:

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

Definiera en utlösare före radering

I det här avsnittet kommer du att se hur du kan definiera en BEFORE DELETE trigger för att förhindra användare från att ta bort specifika poster från en tabell.

  1. För att skapa prevent_delete trigger, kör kommandot nedan:

    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 ;
    

    Denna utlösare kommer att förhindra produkter märkta med värdet ALL i tillgänglighetskolumnen från att tas bort.

  2. Försök sedan att ta bort den första produkten från produkttabellen och se om triggern kommer att anropas:

    DELETE FROM products WHERE product_id='1';
    

    Utdata:

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

Vi har tittat på de olika triggers som anropas före en databasoperation. Därefter kommer vi att undersöka andra typer av triggers som utlöses efter databashändelser.

Skapa efterhändelseutlösare

I en produktionsmiljö kanske du vill att vissa triggers ska exekveras automatiskt efter att en databashändelse inträffar (till exempel infogning av poster i olika tabeller). Exemplen nedan visar hur dessa typer av utlösare kan användas i vår exempeldatabas.

Skapa en efter infogningsutlösare

Det här exemplet skapar en utlösare som heter product_availability som infogar mappningsposter i products_to_stores tabell. Denna utlösare används för att genomdriva affärslogik; i synnerhet hjälper det till att definiera produkttillgängligheten för de olika butikerna.

  1. Kör koden nedan för att skapa product_availability utlösare. Eftersom vi har flera rader kod i triggerkroppen kommer vi att använda en BEGIN...END blockera:

    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 ;
    
    • När en vara infogas i products tabell, kommer utlösaren att kontrollera availability fältet.

    • Om den är märkt med LOCAL värde kommer produkten endast att finnas tillgänglig i en butik.

    • Alla andra värden kommer att instruera utlösaren att göra produkten tillgänglig för de två butikerna som vi skapade tidigare.

  2. För att se product_availability utlösaren i aktion, infoga de två posterna i produkttabellen:

    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. Fråga sedan products_to_stores tabell:

    SELECT * FROM products_to_stores;
    

    Du bör se en utdata som liknar den som visas nedan:

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

Definiera en utlösare efter uppdatering

En trigger kan också aktiveras efter en UPDATE händelse. Vi kommer att se hur vi kan utnyttja denna typ av trigger för att hålla reda på prisförändringar i vår butik över tid.

  1. Skapa en product_history_updater utlösa genom att köra kommandot nedan:

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

    Den här utlösaren registrerar ändringar av en produkts retail_price i products_price_history bord.

    Obs Till skillnad från tidigare exempel har denna utlösare bara ett uttalande i utlösarens kropp, så vi behöver inte ändra DELIMITER .
  2. Testa sedan att uppdatera priset på den första produkten genom att köra kommandot nedan:

    UPDATE products SET retail_price='36.75' WHERE product_id='1';
    
  3. Fråga sedan products_price_history tabell för att se om prisändringen loggades:

    SELECT * FROM products_price_history;
    

    Om utlösaren fungerade som förväntat bör du få följande utdata:

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

Skapa en utlösare efter borttagning

I vissa fall kanske du vill logga borttagningsåtgärder efter att en specifik åtgärd har inträffat i databasen. Du kan uppnå detta genom att använda AFTER DELETE utlösare.

  1. Skapa en ny product_archiver utlösa med kommandot nedan:

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

    Denna utlösare arkiverar borttagna produkter i en separat tabell med namnet archived_products . När en vara raderas från products tabell loggar vår utlösare den automatiskt till archived_products tabell för framtida referens.

  2. Ta sedan bort en produkt från products tabell och se om utlösaren kommer att anropas:

    DELETE FROM products WHERE product_id='3';
    
  3. Om du nu kontrollerar archived_products tabell, bör du se en post:

    SELECT * FROM archived_products;
    

    Utdata:

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

Ta bort en utlösare

Du har sett de olika typerna av triggers och hur de kan användas i en produktionsmiljö. Ibland kanske du vill ta bort en utlösare från databasen.

Du kan ta bort en utlösare om du inte vill använda den längre med syntaxen nedan:

DROP TRIGGER IF EXISTS TRIGGER_NAME;
Obs IF EXISTS nyckelord är en valfri parameter som bara tar bort en utlösare om den finns.

Till exempel för att ta bort product_archiving trigger som vi definierade ovan, använd kommandot nedan:

DROP TRIGGER IF EXISTS product_archiver;

Utdata:

Query OK, 0 rows affected (0.00 sec)
Varning Var försiktig när du tar bort tabeller som är kopplade till utlösare. När en tabell har släppts från MySQL-databasen raderas även de relaterade triggers automatiskt.

Mer information

Du kanske vill konsultera följande resurser för ytterligare information om detta ämne. Även om dessa tillhandahålls i hopp om att de kommer att vara användbara, vänligen observera att vi inte kan garantera noggrannheten eller aktualiteten hos externt värdmaterial.

  • MySQL-utlösarsyntax och exempel

  1. DATEDIFF() Exempel i SQL Server

  2. SQL ELLER Operator för nybörjare

  3. Ogiltigt standardvärde för "dateAdded"

  4. Använd sammansatt primärnyckel som främmande nyckel