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
-
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.
-
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.
-
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.
-
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.
-
Därefter kommer du att se en MySQL-prompt som liknar den som visas nedan:
mysql >
-
Skapa en
test_database
genom att köra kommandot nedan:CREATE DATABASE test_database;
Utdata:
Query OK, 1 row affected (0.02 sec)
-
Växla till databasen:
USE test_database;
Utdata:
Database changed
-
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)
-
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) ...
-
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)
-
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
ochretail_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 enLOCAL
värde. Annars kommer vi att använda värdet förALL
för att beteckna en produkt som är tillgänglig i båda butikerna (Philadelphia och Galloway).
-
-
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) ...
-
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)
-
Därefter kommer produkternas tillgänglighet att mappas till en annan tabell med namnet
products_to_stores
. Den här tabellen refererar bara tillproduct_id
frånproducts
tabellen ochstore_id
frånstores
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)
-
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)
-
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
:AntingenBEFORE
ellerAFTER
. -
TRIGGER_EVENT
:Du måste ange databashändelsen som kommer att anropa triggern:INSERT
,UPDATE
, ellerDELETE
. -
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 ändrarDELIMITER
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.
-
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 kontrolleracost_price
ochretail_price
innan en post infogas iproducts
tabell, med hjälp avIF...THEN...END IF
uttalande. -
Om
cost_price
är större eller lika medretail price
, säger våra utlösare till MySQL att skapa ett anpassat undantag som instruerar användaren att rätta till felet.
-
-
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 äncost_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.
-
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. -
För att anropa
product_name_validator
trigger, kan vi försöka uppdatera namnet på produkten med ID1
: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.
-
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. -
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.
-
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 enBEGIN...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 kontrolleraavailability
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.
-
-
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');
-
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.
-
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
iproducts_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
. -
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';
-
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.
-
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ånproducts
tabell loggar vår utlösare den automatiskt tillarchived_products
tabell för framtida referens. -
Ta sedan bort en produkt från
products
tabell och se om utlösaren kommer att anropas:DELETE FROM products WHERE product_id='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