NetHq Blog


A rossz legjobb kritikája az, ha megmutatjuk, hogyan kell jobban csinálni.

Triggerek, függvények MySQL-ben

Nightvis, 2012.05.04 02:07
Ha már szóba került a trigger mint olyan a jabber-es bejegyzésem kapcsán, úgy döntöttem, hogy leírom mi is az valójában, mert nagyon sokan nincsenek vele tisztában, vagy nem igazán tudják mi fán terem.

Ha már szóba került a trigger mint olyan a jabber-es bejegyzésem kapcsán, úgy döntöttem, hogy leírom mi is az valójában, mert nagyon sokan nincsenek vele tisztában, vagy nem igazán tudják mi fán terem. Ugyan ez igaz a tárolt eljárásokra és a függvényekre is.

Először a triggerekről ejtenék pár szót, ami reményeim szerint bőven elég lesz ahhoz, hogy ha valaki akar, el tudjon indulni a MySQL ezen szolgáltatásának nem éppen rögös útján is. A triggerek valójában eljárások, melyek egy bizonyos táblához vannak kapcsolva, és valamilyen esemény (DML utasítások esetén: insert, update, delete) hatására futnak le. Ezen túlmenően megszabhatjuk azt is, hogy pontosan mikor.

Az esemény előtt vagy után. before insert, after insert, before update, after update, before delete, after delete azaz beszúrás előtt, beszúrás után, frissítés előtt, frissítés után, törlés előtt, törlés után Azt tudni kell, hogy egy eseményhez nem kapcsolhatunk több ugyan arra az eseményre (INSERT, UPDATE, DELETE), ugyan abban az időben (BEFORE, AFTER) aktiválódó triggert.

Ez azt jelenti, hogy nem lehet két BEFORE UPDATE triggered egy táblára, de lehet egy BEFORE UPDATE és AFTER UPDATE, illetőleg az is rendben van, ha BEFORE INSERT és BEFORE UPDATE eseményre állítasz be triggereket. Az is kikötés, hogy átmeneti táblákhoz és nézetekhez sem kapcsolhatunk triggert, továbbá a triggerek nem módosíthatják azokat a táblákat, amikhez kapcsolva vannak.

Tehát, ha a users tábla beszúrás utáni eseményére lefut egy trigger, az nem szúrhat be, nem frissíthet adatot, és nem végezhet törlést a users táblán. Fontos, hogy a DELETE esemény nem fedi le a DROP TABLE és a TRUNCATE parancsokat. Tehát ha eldobod a táblát, vagy kiüríted, a triggered nem fogja törlésnek venni az eseményt, és nem fog lefutni. Jelenleg a triggerek idegen kulcsok (foreign keys) által kiváltott eseményekre nem reagálnak.

Tehát hiába törlődik egy rekordod az idegen kulcsok által másik táblából való törlés hatására, a triggered nem fogja törlésnek tekinteni. Reméljük, ezt mihamarabb javítják.

Triggerek létrehozása... lássunk rá egy példát: Van egy orders táblánk, amiben van egy elsődleges auto_increment id-nk, és egy product_id mezőnk, ami a products tábla szintén auto_increment id mezőjéhez van kapcsolva. A products táblában van továbbá egy amount mező, ami a termék még raktáron lévő mennyiségét jelöli:

create table products 
( id int not null auto_increment, amount not null default 1, primary key (id) );
create table orders
( id int not null auto_increment, product_id not null, amount not null default 1,
primary key (id) );

Szeretnénk, hogy amikor az orders táblába bekerül egy új sor, tehát az egyik termékből vásárolnak x darabot, akkor a products táblában a termék mennyisége x-el csökkenjen. Ezt triggerrel egyszerűen a következő módon tehetjük meg:

DELIMITER // 
CREATE TRIGGER update_product_amount
BEFORE INSERT ON 'orders' FOR EACH ROW
BEGIN UPDATE 'products' SET amount = amount - NEW.amount
WHERE id = NEW.product_id; END; DELIMITER ;
 

Látható, hogy nagyon egyszerűen pár sor segítségével megoldható a dolog, így nem kell PHP-ból plusz lekérdezést küldenünk a MySQL felé, ami ugye nekünk is jó, és a szervernek is. Tehát a trigger létrehozásának formája:

CREATE TRIGGER trigger_neve esemény 
ON táblanév FOR EACH ROW BEGIN utasítások END;

ahol az esemény a fent felsorolt események valamelyike, a táblanév pedig annak a táblának a neve, amelyiknek a változásait/eseményeit "figyeltetni" szeretnénk a triggerrel. A "FOR EACH ROW" azt jelzi, hogy az utasítások minden alkalommal fussanak le, mikor a trigger aktiválódik. A BEGIN és az END csak akkor szükséges, amennyiben bonyolultabb utasításokat szeretnénk írni a trigger végrehajtó részébe. A NEW mellé tartozik természetesen egy OLD is. Ha valaki még nem találta volna ki, ez azt jelzi, hogy az újonnan beszúrt sorra vonatkozik, illetőleg a ponttal mögötte álló mezőre. Ebből fakadóan értelemszerű, hogy beszúrás esetén nem használható az OLD kulcsszó, illetőleg törlés esetén nem elérhető a NEW. A NEW által elért mezőket megváltoztathatjuk, míg az OLD által elérhetőek csak olvashatóak, de remélem ez utóbbit nem kell elmagyaráznom, hogy miért van így :D A NEW által elérhető mezők módosítása.... pl. beszúrásnál, ha azt szeretnénk, hogy a users táblába beszúrt nevek csupa kisbetűvel legyenek írva.:

DELIMITER // 
CREATE TRIGGER lowercase_username
BEFORE INSERT ON 'users'
FOR EACH ROW SET NEW.'username' = lower(NEW.'username'); DELIMITER ;

És ez legyen egyben példa arra is, mikor nem használjuk a BEGIN és az END kulcsszavakat. A már léterhozott triggereinket a

SHOW triggers;

parancs segítségével listázhatjuk, illetőleg a

DROP TRIGGER trigger_neve;

paranccsal törölhetjük. Triggereink sajnos nem tartalmazhatnak olyan utasításokat, amik explicit vagy implicit módon indítanak vagy állítanak le tranzakciókat (START TRANSACTION, COMMIT, ROLLBACK).

Tartalom közösségi megosztása: