En trigger er en foruddefineret SQL-kommando, der automatisk udføres, når der sker specifikke handlinger i databasen. Den kan udløses enten før eller efter en INSERT
, UPDATE
eller DELETE
begivenhed.
Triggere bruges hovedsageligt til at vedligeholde softwarelogik i MySQL-serveren, og de har flere fordele:
-
Triggere hjælper med at holde globale operationer centraliseret på ét sted.
-
De reducerer kode på klientsiden og hjælper med at minimere de rundrejser, der foretages til databaseserveren.
-
De hjælper med at gøre applikationer mere skalerbare på tværs af forskellige platforme.
Nogle almindelige use-cases af triggere omfatter revisionslogning, forudberegning af databaseværdier (f.eks. kumulative summer) og håndhævelse af komplekse dataintegritet og valideringsregler.
I denne guide lærer du:
-
Hvordan syntaksen for en trigger er opbygget.
-
Sådan opretter du triggere, der udføres, før andre databasehændelser opstår.
-
Sådan oprettes triggere, der udføres efter andre databasehændelser opstår.
-
Sådan sletter du triggere.
Før du begynder
-
Hvis du ikke allerede har gjort det, skal du oprette en Linode-konto og Compute Instance. Se vores vejledninger Kom godt i gang med Linode og Oprettelse af en beregningsinstans.
-
Følg vores guide til konfiguration og sikring af en computerinstans for at opdatere dit system. Du ønsker måske også at indstille tidszonen, konfigurere dit værtsnavn, oprette en begrænset brugerkonto og skærpe SSH-adgang.
-
En MySQL-server og klient installeret på Linode-serveren. Installationsvejledninger til MySQL er tilgængelige for forskellige distributioner i vores MySQL-sektion.
Forbered databasen
For bedre at forstå, hvordan triggere virker, vil vi oprette en prøvedatabase og tilføje eksempeldata til den. Senere vil vi oprette forskellige triggere på databasen som en proof of concept-øvelse.
-
Først skal du logge ind på din MySQL-server:
mysql -u root -p
Indtast derefter root-adgangskoden til din MySQL-server og tryk på Enter for at fortsætte.
-
Dernæst vil du se en MySQL-prompt, der ligner den, der er vist nedenfor:
mysql >
-
Opret en
test_database
ved at køre kommandoen nedenfor:CREATE DATABASE test_database;
Output:
Query OK, 1 row affected (0.02 sec)
-
Skift til databasen:
USE test_database;
Output:
Database changed
-
Når databasen er valgt, vil vi oprette nogle tabeller, som vi vil bruge til at demonstrere triggere. Vi begynder med at oprette
stores
bord. Denne tabel vil indeholde oplysninger om to eksempler på butikker/kontorer, hvor vores hypotetiske virksomhed opererer fra:CREATE TABLE stores ( store_id BIGINT PRIMARY KEY AUTO_INCREMENT, store_name VARCHAR(50) ) ENGINE=InnoDB;
Output:
Query OK, 0 rows affected (0.07 sec)
-
Tilføj derefter to poster til
stores
tabel ved at køre kommandoerne nedenfor:INSERT INTO stores (store_name) VALUES ('Philadelphia'); INSERT INTO stores (store_name) VALUES ('Galloway');
Efter hver kommando får du nedenstående output:
Query OK, 1 row affected (0.08 sec) ...
-
Bekræft registreringerne ved at køre kommandoen nedenfor:
SELECT * FROM stores;
Output:
+----------+--------------+ | store_id | store_name | +----------+--------------+ | 1 | Philadelphia | | 2 | Galloway | +----------+--------------+ 2 rows in set (0.01 sec)
-
Opret derefter
products
bord. Bordet vil indeholde forskellige produkter, der tilbydes i butikken: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;
Output:
Query OK, 0 rows affected (0.13 sec)
-
Hvert produkt vil blive entydigt identificeret med et
product_id
. -
Et
product_name
feltet angiver navnene på emnerne. -
cost_price
ogretail_price
felter bestemmer henholdsvis købs- og salgsprisen. -
En
availability
kolonnen vil definere produkttilgængeligheden i de forskellige butikker. Hvis produktet kun er tilgængeligt i vores lokale butik (Philadelphia), vil vi angive det med enLOCAL
værdi. Ellers vil vi bruge værdien af ALL
for at betegne et produkt, der er tilgængeligt i begge butikker (Philadelphia og Galloway).
-
-
Tilføj eksempeldata til
products
tabel: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 vil få output vist nedenfor efter hver indsæt kommando:
Query OK, 1 row affected (0.02 sec) ...
-
Bekræft om produkterne blev indsat ved at køre kommandoen nedenfor:
SELECT * FROM products;
Output:
+------------+----------------+------------+--------------+--------------+ | 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)
-
Dernæst vil produkternes tilgængelighed blive knyttet til en anden tabel med navnet
products_to_stores
. Denne tabel vil kun referere tilproduct_id
fraproducts
tabellen ogstore_id
frastores
tabel, hvor varen er tilgængelig.Opret
products_to_stores
tabel ved at køre koden nedenfor:CREATE TABLE products_to_stores ( ref_id BIGINT PRIMARY KEY AUTO_INCREMENT, product_id BIGINT, store_id BIGINT ) ENGINE=InnoDB;
Output:
Query OK, 0 rows affected (0.14 sec)
-
Dernæst vil vi oprette en
archived_products
bord. Tabellen vil indeholde oplysninger om slettede produkter til fremtidig reference:CREATE TABLE archived_products ( product_id BIGINT PRIMARY KEY , product_name VARCHAR(40), cost_price DOUBLE, retail_price DOUBLE, availability VARCHAR(5) ) ENGINE=InnoDB;
Output:
Query OK, 0 rows affected (0.14 sec)
-
Til sidst vil vi oprette en
products_price_history
tabel til sporing af de forskellige priser på hvert produkt over tid:CREATE TABLE products_price_history ( product_id BIGINT PRIMARY KEY AUTO_INCREMENT, price_date DATETIME, retail_price DOUBLE ) ENGINE=InnoDB;
Output:
Query OK, 0 rows affected (0.14 sec)
Når vores databasestruktur er på plads, kan vi nu gå videre og lære den grundlæggende syntaks for en MySQL-databasetrigger for at skabe vores første eksempel.
Triggersyntaks
Som tidligere nævnt udløses triggere automatisk enten før eller efter, at en SQL-kommando køres i databasen. Den grundlæggende syntaks til at oprette triggere er som følger:
CREATE TRIGGER TRIGGER_NAME
TRIGGER_TIME TRIGGER_EVENT
ON TABLE_NAME FOR EACH ROW
[TRIGGER BODY];
-
TRIGGER_NAME
:Hver trigger skal have et unikt navn, og du bør definere det her. -
TRIGGER_TIME
:EntenBEFORE
ellerAFTER
. -
TRIGGER_EVENT
:Du skal angive den databasehændelse, der vil påkalde triggeren:INSERT
,UPDATE
ellerDELETE
. -
TRIGGER BODY
:Dette specificerer den faktiske SQL-kommando (eller -kommandoer), som du ønsker skal køres af din trigger.
Hvis en udløserbody har mere end én SQL-sætning, skal du omslutte den i en BEGIN...END
blok. Du bliver også nødt til midlertidigt at ændre DELIMITER
der signalerer slutningen af triggerkroppen til en ny værdi. Dette sikrer, at udsagn i kroppen ikke fortolkes for tidligt af din MySQL-klient. Et eksempel på dette ser ud som følgende:
DELIMITER &&
CREATE TRIGGER TRIGGER_NAME
TRIGGER_TIME TRIGGER_EVENT
ON TABLE_NAME FOR EACH ROW
BEGIN
[TRIGGER BODY]
END &&
DELIMITER ;
Bemærk Den sidste linje i dette eksempel ændrerDELIMITER
tilbage til standard;
værdi.
Oprettelse af før hændelsesudløsere
I dette afsnit vil vi se på de forskellige typer triggere, der udløses før en databaseoperation. Disse inkluderer BEFORE INSERT
, BEFORE UPDATE
, og BEFORE DELETE
udløser.
Oprettelse af en før indsættelsesudløser
Vi vil oprette vores første BEFORE INSERT
udløser. Udløseren sørger for, at udsalgsprisen for et produkt er større end kostprisen, hver gang varer indsættes i products
bord. Ellers vil databasebrugeren få en fejl.
-
Mens du stadig er på
mysql >
prompt, skal du indtaste kommandoen nedenfor: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 ;
-
Ovenstående kode definerer triggernavnet (
price_validator
), tid (BEFORE
), hændelse (INSERT
), og tabellen (products
) for at blive påvirket. -
Vores trigger bruger
NEW
søgeord for at kontrollerecost_price
ogretail_price
før en post indsættes iproducts
tabel ved hjælp afIF...THEN...END IF
erklæring. -
Hvis
cost_price
er større eller lig medretail price
, fortæller vores triggere MySQL om at kaste en tilpasset undtagelse, der instruerer brugeren om at rette fejlen.
-
-
For at teste triggeren ovenfor, prøv at indsætte et produkt, der overtræder valideringsreglen:
INSERT INTO products (product_name, cost_price, retail_price, availability) VALUES ('GAMING MOUSE PAD', '145.00', '144.00','LOCAL');
Output:
ERROR 1644 (45000): Retail price must be greater than cost price.
Ovenstående indsæt-kommandoer skulle mislykkes, fordi
retail_price
(144,00) er ikke større endcost_price
(145,00).
Oprettelse af en før opdateringsudløser
Dernæst vil vi oprette en BEFORE UPDATE
udløser. Denne trigger forhindrer databasebrugere i at redigere et produktnavn, når et produkt er blevet indsat i databasen. Hvis du har flere brugere, der arbejder i databasen, en BEFORE UPDATE
trigger kan bruges til at gøre værdier skrivebeskyttet, og dette kan forhindre ondsindede eller skødesløse brugere i at ændre registreringer unødigt.
-
Opret en ny
product_name_validator
trigger med kommandoen nedenfor: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 ;
Denne trigger sammenligner værdierne for det nye
product_name
(NEW.product_name
) og det gamle navn, der allerede er i databasen (OLD.product_name
). Hvis der er et misforhold, bliver der kastet en undtagelse. -
For at påkalde
product_name_validator
trigger, kan vi forsøge at opdatere navnet på produktet med ID'et1
:UPDATE products SET product_name='WIRELESS BLUETOOTH MOUSE' WHERE product_id='1';
Output:
ERROR 1644 (45000): Product name is read-only and it can not be changed.
Definition af en før sletning-trigger
I dette afsnit vil du se, hvordan du kan definere en BEFORE DELETE
trigger for at forhindre brugere i at slette specifikke poster fra en tabel.
-
For at oprette
prevent_delete
trigger, skal du køre kommandoen nedenfor: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 ;
Denne trigger forhindrer produkter markeret med værdien
ALL
i tilgængelighedskolonnen fra at blive slettet. -
Prøv derefter at slette det første produkt fra produkttabellen og se, om triggeren vil blive aktiveret:
DELETE FROM products WHERE product_id='1';
Output:
ERROR 1644 (45000): The product can not be deleted because it is available in ALL stores.
Vi har set på de forskellige triggere, der aktiveres før en databaseoperation. Dernæst vil vi se på de andre typer triggere, der udløses efter databasehændelser.
Oprettelse af efterhændelsestriggere
I et produktionsmiljø ønsker du måske, at nogle triggere udføres automatisk, efter at der opstår en databasehændelse (f.eks. indsættelse af poster i forskellige tabeller). Eksemplerne nedenfor viser, hvordan denne slags triggere kan bruges i vores eksempeldatabase.
Oprettelse af en After Insert Trigger
Dette eksempel opretter en trigger ved navn product_availability
der indsætter kortlægningsposter i products_to_stores
bord. Denne trigger bruges til at håndhæve forretningslogik; det hjælper især med at definere produkttilgængeligheden for de forskellige butikker.
-
Kør koden nedenfor for at oprette
product_availability
udløser. Da vi har flere linjer kode i udløserlegemet, vil vi bruge enBEGIN...END
blokere: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 vare indsættes i
products
tabel, vil udløseren kontrollereavailability
felt. -
Hvis det er markeret med
LOCAL
værdi, vil produktet kun være tilgængeligt i én butik. -
Enhver anden værdi vil instruere triggeren om at gøre produktet tilgængeligt for de to butikker, vi oprettede tidligere.
-
-
For at se
product_availability
trigger i aktion, indsæt de to poster 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');
-
Forespørg derefter
products_to_stores
tabel:SELECT * FROM products_to_stores;
Du bør se et output svarende til det, der er vist nedenfor:
+--------+------------+----------+ | ref_id | product_id | store_id | +--------+------------+----------+ | 1 | 4 | 1 | | 2 | 5 | 1 | | 3 | 5 | 2 | +--------+------------+----------+ 3 rows in set (0.00 sec)
Definition af en efter opdateringsudløser
En trigger kan også udløses efter en UPDATE
begivenhed. Vi vil se, hvordan vi kan udnytte denne type trigger til at holde styr på prisændringer i vores butik over tid.
-
Opret en
product_history_updater
trigger ved at køre kommandoen nedenfor: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);
Denne trigger registrerer ændringer af et produkts
retail_price
iproducts_price_history
tabel.Bemærk I modsætning til tidligere eksempler har denne udløser kun én sætning i udløserens krop, så vi behøver ikke at ændre
DELIMITER
. -
Prøv derefter at opdatere prisen på det første produkt ved at køre kommandoen nedenfor:
UPDATE products SET retail_price='36.75' WHERE product_id='1';
-
Forespørg derefter
products_price_history
tabel for at se, om prisændringen blev logget:SELECT * FROM products_price_history;
Hvis triggeren virkede som forventet, skulle du få nedenstående output:
+------------+---------------------+--------------+ | product_id | price_date | retail_price | +------------+---------------------+--------------+ | 1 | 2020-01-28 11:46:21 | 36.75 | +------------+---------------------+--------------+ 1 row in set (0.00 sec)
Oprettelse af en After Delete Trigger
I nogle tilfælde vil du måske logge slettehandlinger, efter at en bestemt handling har fundet sted i databasen. Du kan opnå dette ved at bruge AFTER DELETE
trigger.
-
Opret en ny
product_archiver
trigger med kommandoen nedenfor: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);
Denne udløser arkiverer slettede produkter i en separat tabel med navnet
archived_products
. Når en vare slettes fra de vigtigsteproducts
tabel, vil vores trigger automatisk logge den tilarchived_products
tabel til fremtidig reference. -
Derefter skal du slette et produkt fra
products
tabel og se, om udløseren vil blive aktiveret:DELETE FROM products WHERE product_id='3';
-
Nu, hvis du tjekker
archived_products
tabel, bør du se én post:SELECT * FROM archived_products;
Output:
+------------+--------------+------------+--------------+--------------+ | product_id | product_name | cost_price | retail_price | availability | +------------+--------------+------------+--------------+--------------+ | 3 | SMART WATCH | 189.6 | 225.3 | LOCAL | +------------+--------------+------------+--------------+--------------+ 1 row in set (0.00 sec)
Sletning af en trigger
Du har set de forskellige typer af triggere, og hvordan de kan bruges i et produktionsmiljø. Nogle gange vil du måske fjerne en trigger fra databasen.
Du kan slette en trigger, hvis du ikke ønsker at bruge den længere ved at bruge syntaksen nedenfor:
DROP TRIGGER IF EXISTS TRIGGER_NAME;
Bemærk IF EXISTS
søgeord er en valgfri parameter, der kun sletter en trigger, hvis den findes.
For eksempel at slette product_archiving
trigger, som vi definerede ovenfor, brug nedenstående kommando:
DROP TRIGGER IF EXISTS product_archiver;
Output:
Query OK, 0 rows affected (0.00 sec)
Forsigtig Vær forsigtig, når du sletter tabeller forbundet med triggere. Når en tabel er slettet fra MySQL-databasen, slettes de relaterede triggere også automatisk.
Flere oplysninger
Du ønsker måske at konsultere følgende ressourcer for yderligere oplysninger om dette emne. Selvom disse leveres i håb om, at de vil være nyttige, bemærk venligst, at vi ikke kan stå inde for nøjagtigheden eller aktualiteten af eksternt hostede materialer.
- MySQL Trigger-syntaks og eksempler