Șansele sunt, știi ce este un declanșator al bazei de date, cel puțin în termeni conceptuali. Șansele sunt chiar mai mari, deoarece știi că MySQL suportă declanșatoarele și le-a sprijinit de ceva timp. Aș ghici, chiar și înarmat cu această cunoaștere, că mulți dintre voi nu profitați de declanșarea cu MySQL. Acestea sunt unul dintre acele lucruri care ar trebui să fie absolut în setul de instrumente de dezvoltare, deoarece acestea pot schimba într-adevăr modul în care vă uitați la datele dvs..
"Cu toate acestea, pe măsură ce aplicațiile cresc din ce în ce mai complicate, cu atât mai mult putem abstracționa straturile unei aplicații pentru a rezolva ceea ce ar trebui, cu atât mai mare este gradul de utilizare internă a dezvoltării noastre".
Pentru cei neinițiați, o declanșare este o regulă pe care o puneți pe o masă care spune în mod fundamental, de fiecare dată când DELETE, UPDATE sau INSERT ceva în acest tabel, faceți și altceva. De exemplu, este posibil să dorim să înregistrăm o schimbare, dar în loc să scriem două interogări separate, unul pentru schimbare și unul pentru jurnal, putem scrie un declanșator care spune: "Ori de câte ori acest rând este actualizat, creați un rând nou într-un alt tabel pentru a-mi spune că actualizarea a fost făcută ". Se adaugă un pic deasupra capului interogării inițiale, dar deoarece nu există două pachete care călătoresc în baza dvs. de date pentru a face două lucruri separate, există un câștig general de performanță (teoretic, oricum).
Triggerele au fost introduse în MySQL în versiunea 5.0.2. Sintaxa pentru un declanșator este puțin străină la prima culoare. MySQL utilizează standardul ANSI SQL: 2003 pentru proceduri și alte funcții. Dacă sunteți confortabil cu un limbaj de programare în general, nu este atât de greu de înțeles. Specificația nu este disponibilă în mod liber, așadar voi face tot posibilul pentru a folosi structuri simple și pentru a explica ce se întâmplă în declanșator. Veți avea de-a face cu aceleași structuri logice pe care le oferă orice limbaj de programare.
După cum am menționat mai sus, declanșatoarele vor fi executate procedural pe evenimentele UPDATE, DELETE și INSERT. Ceea ce nu am menționat este că pot fi executate fie înainte, fie după evenimentul definit. Prin urmare, ați putea avea un declanșator care se va declanșa înainte de DELETE sau după DELETE, așa mai departe și așa mai departe. Aceasta înseamnă că ați putea avea un declanșator care se declanșează înainte de INSERT și unul separat care se declanșează DUPĂ un INSERT, care poate fi foarte puternic.
Mă voi uita la trei utilizări pe care le puteți lua în considerare adăugând la setul de instrumente. Există mai multe utilizări la care nu voi fi delfinat, deoarece simt că există metode mai bune pentru a obține aceleași rezultate sau merită propriul tutorial. Fiecare dintre aceste utilizări pe care le explorez are un omolog în stratul logic al serverului și nu sunt concepte noi. Cu toate acestea, pe măsură ce aplicațiile cresc din ce în ce mai complicate, cu atât mai mult putem să abrogem straturile unei aplicații pentru a trata ceea ce ar trebui, cu atât mai mare este gradul de utilizare internă a dezvoltării noastre.
Lucrez cu un sistem de coșuri mitic, cu articole care au prețuri. Am încercat să păstrez structura de date cât mai simplă posibil doar pentru scopuri ilustrative. Denumesc coloane și mese în scopul înțelegerii, și nu pentru utilizarea în producție. De asemenea, folosesc TIMESTAMPS mai degrabă decât alte alternative pentru ușurință. Pentru cei care joacă versiunea at-home a jocului de astăzi, folosesc numele de tabelă de căruțe, cart_items, cart_log, elemente, items_cost.
Rețineți că pe parcursul acestui tutorial voi folosi interogări foarte simple pentru a-mi exprima punctele. Nu sunt obligatorie nici o variabilă, deoarece nu folosesc nici o intrare de utilizator. Vreau să fac ca interogările să fie cât mai ușor de citit, dar nu folosiți acest tutorial pentru alte aplicații decât aplicații de declanșare practice. Știu că s-ar putea să fie un comentariu sau două despre acest lucru, deci ia în considerare acest lucru renunțarea mea la răspundere.
Folosesc Particle Tree PHP Quick Profiler pentru a vedea timpii de execuție. De asemenea, folosesc stratul de abstractizare al bazei de date furnizat în instrument doar pentru binele meu. Este un instrument drăguț și face mult mai mult decât doar furnizarea de timpi de execuție SQL.
De asemenea, folosesc Chive pentru a ilustra efectele DB și a crea declanșatoarele mele. Chive este numai MySQL 5+, și este foarte similar cu PHPMyAdmin. Este mai frumos, dar, de asemenea, mult buggier în acest moment. Folosesc Chive, pur și simplu pentru că oferă fotografii bune pe ecran în ceea ce privește ceea ce se întâmplă cu interogările.
O altă notă rapidă. Este posibil să trebuiască să schimbați delimitatorul pentru MySQL în timp ce creați un declanșator. Delimiterul natural pentru MySQL este; dar din moment ce vom folosi acest delimiter pentru interogările adăugate, este posibil să trebuiască să redenumiți explicit delimitatorul dacă le creați prin linia de comandă. Am ales să nu arăt acest lucru, deoarece folosind Chive, nu este nevoie să schimbați delimitatorul.
Pentru a schimba un delimiter, ați face acest lucru înainte de comanda dvs. de declanșare:
DELIMITER $$
Și după comandă de declanșare:
DELIMITER;
Dacă faceți chiar și cea mai mică normalizare a structurii bazei dvs. de date, probabil că ați trecut într-o perioadă în care ați șters sursa principală de date, dar totuși aveți fragmente care rulează în fluxul de date. De exemplu, este posibil să aveți un cart_id care este referit în două sau trei tabele fără chei străine, mai ales că cheile externe nu sunt acceptate cu motorul MyISAM.
Ceea ce ați făcut probabil în trecut este ceva de genul acesta (simplificat pentru ilustrare):
$ sql = 'Șterge din no_trigger_cart_items WHERE cart_id = 1'; $ rs = $ this-> db-> interogare ($ sql); $ sql = 'Șterge din no_trigger_carts WHERE cart_id = 1'; $ rs = $ this-> db-> interogare ($ sql);
Acum, în funcție de cât de bine vă organizați, este posibil să aveți un singur API sau o metodă pe care să o eliminați. În acest caz, ați izolat logica pentru a rula aceste două interogări. Dacă nu este cazul, atunci trebuie să vă amintiți întotdeauna să ștergeți articolele de coș atunci când ștergeți un anumit coș. Nu este dificil, dar când uitați, vă pierdeți integritatea datelor.
Introduceți declanșatorul nostru. Voi crea un declanșator foarte simplu, astfel încât ori de câte ori să șterg un cărucior, declanșatorul meu va declanșa ștergerea oricăror articole de coș care au același cart_id:
CREATE TRIGGER 'tutorial'. 'Înainte_delete_carts' ÎNAINTE DE DELETE pe 'trigger_carts' PENTRU FIECARE LINIE BEGIN DELETE FROM trigger_cart_items WHERE OLD.cart_id = cart_id; SFÂRȘIT
Sintaxă foarte simplă, așa cum am spus mai sus. Să trecem prin fiecare linie.
Prima mea linie afirmă "tutorialul CREATE TRIGGER". 'Before_delete_carts' ". Spun MySQL să creeze un declanșator în baza de date "tutorial" pentru a avea un nume de "before_delete_carts". Tind să-mi numesc declanșatoarele cu formula "When_How_Table". Asta lucrează pentru mine, dar există o mulțime de alte modalități de a face acest lucru.
A doua linie spune MySQL definiția acestui declanșator, "înainte de a șterge" trigger_carts "FOR EACH ROW". Îi spun MySQL că înainte de a șterge pe această masă, pentru fiecare rând face ceva. Acest lucru este explicat în continuare în cadrul BEGIN și END. "DELETE FROM trigger_cart_items WHERE OLD.cart_id = cart_id;" Spun MySQL înainte de a șterge de la trigger_carts, luați OLD.cart_id și, de asemenea, ștergeți de la trigger_cart_items. Sintaxa veche este variabila definită. Vom discuta acest lucru în următoarea secțiune în care vom combina OLD și NEW.
Nu există nimic de creat acest declanșator. Avantajul este să vă mișcați logica integrității datelor în stratul de date, ceea ce aș putea face, este locul unde aparține. Există, de asemenea, un alt avantaj ușor și acesta este câștigul ușor de performanță, văzut mai jos.
Două interogări:
O întrebare cu un declanșator:
După cum puteți vedea, există un câștig ușor de performanță, care ar trebui să fie așteptat. Baza de date pe care o folosesc este pe localhost cu serverul meu, dar dacă folosesc un server DB separat, câștigul meu de performanță ar fi puțin mai mare datorită timpului de transfer între cele două servere. Ștergerea declanșatorului meu are un timp puțin mai mare pentru a fi șters, dar există o singură interogare, deci timpul total scade. Înmulțiți acest lucru peste tot codul pe care îl utilizați pentru a vă menține integritatea datelor și câștigul de performanță devine cel puțin modest.
O notă despre performanță, prima dată când declanșatorul funcționează, poate fi mult mai lent decât timpul ulterior. Nu folosesc declanșatori în mod necesar pentru câștigul de performanță, ci mai degrabă pentru a mișca logica datelor mele în stratul de date, așa cum doriți să mutați prezentarea de la marcajul dvs. la stratul de prezentare, altfel cunoscut sub numele de CSS.
Următorul exemplu pe care îl vom analiza se va ocupa de logare. Spune că vreau să țină evidența fiecărui articol plasat într-un cărucior. Poate că vreau să monitorizez rata de cumpărare a articolelor mele. Poate că vreau doar să am o copie a fiecărui articol plasat într-un cărucior, neapărat vândut, doar pentru o înțelegere a minții clienților mei. Poate că ați creat articolele de coș ca tabel MEMORY și doriți să înregistrați toate elementele într-un tabel InnoDB. Indiferent de motiv, să examinăm un declanșator INSERT, care va deschide câteva posibilități bune pentru logarea sau auditarea datelor noastre.
Înainte de declanșare, probabil am făcut ceva de genul acesta (din nou, simplificat pentru ilustrare):
Acum, putem crea un declanșator foarte simplu pentru acest proces de logare:
CREATE TRIGGER 'after_insert_cart_items' DUPA INSERTA ON 'trigger_cart_items' PENTRU FIECARE LINIE BEGIN INSERT IN trigger_cart_log (cart_id, item_id) VALUES (NEW.cart_id, NEW.item_id); SFÂRȘIT
Să trecem prin asta din nou, așa că există claritate cu privire la ceea ce face acest declanșator. Mai întâi începem cu linia "CREATE TRIGGER" after_insert_cart_items "". Îi spun din nou lui MySQL să creeze un declanșator cu numele "after_insert_cart_items". Numele ar putea fi "Foo" sau "BullWinkle" sau orice doriți să-l numiți, dar din nou, prefer să ilustrez numele meu de declanșare. Apoi vedem, "după ce introduceți" trigger_cart_items "FOR EACH ROW". Din nou, acest lucru se spune după ce introducem ceva pe trigger_cart_items, pentru fiecare rând inserat executați ceea ce este între BEGIN și END.
În cele din urmă, avem doar "INSERT IN trigger_cart_log (cart_id, item_id) VALUES (NEW.cart_id, NEW.item_id);" care este o interogare standard, cu excepția celor două valori. Folosesc valoarea NOI introdusă în tabela cart_items.
Și ne-am tăiat întrebările în jumătate cu câștigul de performanță subtil:
Și doar pentru a verifica dacă declanșatorul nostru funcționează, văd valorile din tabelul meu:
Acest lucru este din nou, relativ ușor, dar lucrăm cu câteva valori, ceea ce poate adăuga la complexitate doar un pic. Să ne uităm la ceva mai greu.
În acest moment, putem trece peste modul vechi al mai multor interogări cu o singură interogare. Îmi imaginez că va primi doar un pic plictisitor pentru a continua măsurarea performanței interogărilor. În schimb, hai să intrăm în câteva exemple avansate de declanșatoare.
Logica de afaceri este în cazul în care bug-uri întotdeauna creep în sus. Indiferent de cât de atent sau organizat suntem, ceva intră mereu prin fisuri. Declanșatoarele de pe UPDATE atenuează doar puțin. Avem o anumită putere într-un declanșator pentru a evalua valoarea OLD și pentru a stabili valoarea NOU pe baza evaluării. Spune, de exemplu, că dorim ca prețul nostru de articole să fie întotdeauna un marcaj de 30% al costului articolelor. Este logic atunci când, atunci când UPDATE costul nostru, avem, de asemenea, să UPDATE prețul nostru. Să ne ocupăm de asta cu un declanșator.
CREATE TRIGGER 'after_update_cost' DUPA UPDATE ON 'trigger_items_cost' PENTRU ORICE ROW BEGIN UPDATE trigger_items SET pret = (NEW.cost * 1.3) WHERE item_id = NEW.item_id; SFÂRȘIT
Ceea ce facem este actualizarea tabelului de articole cu un preț bazat pe timpul NEW.cost 1.3. Am intrat la un cost de 50 $, așa că noul meu preț ar trebui să fie de 65 $.
Destul de sigur, acest declanșator a funcționat, de asemenea.
Trebuie să aruncăm o privire la un exemplu mai avansat. Avem deja regula de a modifica prețul unui element pe baza costului acestuia. Să presupunem că vrem să-ți costăm puțin costul. Dacă costul este mai mic de 50 USD, costul nostru este de fapt 50 USD. În cazul în care costul este de peste $ 50, dar mai puțin de $ 100, atunci costul nostru devine $ 100 de dolari. În timp ce exemplul meu probabil nu se potrivește cu o regulă de afaceri adevărată, facem să ajustăm costul pe baza factorilor de zi cu zi. Încerc doar să păstrez exemplul ușor de înțeles.
Pentru a face acest lucru, vom lucra din nou cu un UPDATE, dar de data aceasta îl vom trage înainte să executăm interogarea noastră. De asemenea, vom lucra cu o declarație IF, care este disponibilă pentru noi.
Iată noul declanșator:
CREATE TRIGGER 'before_update_cost' ÎNAINTE DE UPDATE ON 'trigger_items_cost' PENTRU FIECARE LINIE ÎNCEPUT DACĂ NEW.cost < 50 THEN SET NEW.cost = 50; ELSEIF NEW.cost > 50 și NEW.cost < 100 THEN SET NEW.cost = 100; END IF; END
Ceea ce facem acum nu este o chemare la o interogare, ci mai degrabă o depășire a valorii. Spun daca costul este mai mic de 50 de dolari, atunci faceti doar 50 de dolari. Dacă costul se situează între 50 și 100 de dolari, atunci faceți 100 de dolari. Dacă este mai presus de asta, atunci l-am lăsat să rămână la fel. Sintaxa mea aici nu este străină de orice altă limbă a serverului. Trebuie să închidem clauza noastră IF cu un END IF; dar altul decât acesta, într-adevăr nu este complicat.
Doar pentru a verifica dacă funcționează declanșatorul nostru, am introdus o valoare de 30 USD pentru cost și ar trebui să fie de 50 USD:
Când intru în costul de 85 $, iată valoarea:
Și, doar pentru a verifica dacă declanșatorul meu AFTER UPDATE încă funcționează, prețul meu ar trebui acum să fie de 130 $:
Viata e buna.
Am atins numai vârful aisbergului cu declanșatori și MySQL. În timp ce există nenumărate utilizări pentru declanșatoarele, m-am descurcat foarte bine în trecut fără ei, trăgând datele mele în stratul meu logic. Acestea fiind spuse, capacitatea de a adăuga reguli datelor mele în stratul de date are sens. Când adăugați îmbunătățiri modeste de performanță, avantajul este și mai mare.
Avem de-a face acum cu aplicații complexe de trafic intens. În timp ce folosiți un declanșator într-o singură pagină, este posibil să nu fie cea mai bună utilizare a timpului și a energiei; un declanșator al unei aplicații web complexe ar putea face lumea diferită. Sper că v-ați bucurat de exemple și vă rog să-mi spuneți ce necesită explicații suplimentare.