Introduksjon til MySQL utløser

Sjansene er at du vet hva en databaseutløser er, i det minste i begrepsbetingelser. Sjansene er enda større at du vet at MySQL støtter utløser og har støttet dem for en stund. Jeg vil gjette, selv bevæpnet med denne kunnskapen, at en god del av dere ikke utnytter triggere med MySQL. De er en av de tingene som absolutt bør være i utviklingsverktøyskassen, da de virkelig kan endre måten du ser på dataene dine.


Innledning: Hva er en Trigger

"Men da applikasjoner blir mer og mer kompliserte, jo lenger kan vi abstrahere lagene i et program for å håndtere det de burde, desto større blir vår brukervennlighet for intern utvikling."

For uinitiert er en utløser en regel som du legger på et bord som i utgangspunktet sier, når du sletter, oppdaterer eller legger inn noe i dette bordet, gjør du også noe annet. For eksempel vil vi kanskje logge en endring, men i stedet for å skrive to separate spørringer, en for endringen og en for loggen, kan vi i stedet skrive en utløser som sier "Når denne raden er oppdatert, opprett en ny rad i et annet bord for å fortelle meg at oppdateringen ble gjort ". Det legger litt overhead til det første spørsmålet, men siden det ikke er to pakker som reiser til databasen din for å gjøre to separate ting, er det en samlet ytelsesgevinst (i teorien allikevel).

Utløsere ble introdusert i MySQL i versjon 5.0.2. Syntaxen for en utløser er litt fremmed ved første rødme. MySQL bruker ANSI SQL: 2003-standarden for prosedyrer og andre funksjoner. Hvis du er komfortabel med et programmeringsspråk generelt, er det ikke så vanskelig å forstå. Spesifikasjonen er ikke fritt tilgjengelig, så jeg vil gjøre mitt beste for å bruke enkle strukturer og forklare hva som skjer i utløseren. Du vil håndtere de samme logiske strukturer som noen programmeringsspråk gir.

Som nevnt ovenfor vil utløsere bli utført prosedyre på UPDATE, DELETE og INSERT hendelser. Det jeg ikke nevnte er at de kan utføres enten før eller etter hendelsen definert. Derfor kan du ha en utløser som vil brenne før en DELETE eller etter en DELETE, så videre og så videre. Dette betyr at du kan ha en utløser som brenner før en INSERT og en separat som brenner AFTER en INSERT, som kan være veldig kraftig.

Jeg skal se på tre bruksområder som du kan vurdere å legge til i verktøykassen din. Det er flere bruksområder som jeg ikke vil dykke på, da jeg føler at det finnes bedre metoder for å få de samme resultatene, eller de fortjener sin egen opplæring. Hver av disse brukerne som jeg utforsker, har en motpart i logikklaget på serversiden, og er ikke nye konsepter. Men da applikasjoner vokser mer og mer komplisert, jo lenger kan vi abstrahere lagene i et program for å håndtere det de burde, desto større blir vår brukervennlighet internt.


Begynnelser: Min tabellstruktur, Verktøy og notater

Jeg jobber med et mytisk vogn system, med elementer som har priser. Jeg har forsøkt å holde datastrukturen så enkel som mulig bare for illustrasjonsformål. Jeg navngir kolonner og tabeller for å forstå, og ikke for produksjon, bruk. Jeg bruker også TIMESTAMPS i stedet for andre alternativer for enkelhet. For de som spiller hjemme-versjonen av dagens spill, bruker jeg tabellens navn på vogner, cart_items, cart_log, items, items_cost.

Vær oppmerksom på gjennom denne opplæringen vil jeg bruke veldig enkle spørringer for å uttrykke mine poeng. Jeg er ikke bindende noen variabel, siden jeg ikke bruker noen brukerinngang. Jeg vil gjøre spørringene så enkle å lese som mulig, men bruk ikke denne opplæringen for annet enn praktisk utløserprogrammer. Jeg vet at det kan være en kommentar eller to om dette, så vurder dette min ansvarsfraskrivelse.

Jeg bruker Particle Tree PHP Quick Profiler for å se kjøretid. Jeg benytter også databasen abstraksjon laget som er gitt i verktøyet bare for min egen fordel. Det er et fint verktøy, og gjør mye mer enn bare å levere SQL-kjøretider.

Jeg bruker også Chive til å illustrere DB-effektene og lage mine utløsere. Biff er bare MySQL 5+, og ligner veldig PHPMyAdmin. Det er finere, men også mye buggier for øyeblikket. Jeg bruker Chive, bare fordi det gir gode skjermbilder om hva som skjer med spørringene.

En annen rask notat. Du må kanskje endre avgrensningen for MySQL mens du lager en trigger. Den naturlige avgrensningen for MySQL er; men siden vi vil bruke denne avgrensningen for våre tilføyde spørringer, må du kanskje omdøpe delimiteren eksplisitt dersom du lager disse via kommandolinjen. Jeg har valgt å ikke vise dette, fordi bruk av Chive, det er ikke nødvendig å endre avgrensningen.

For å endre en avgrensning, ville du bare gjøre dette før utløserkommandoen din:

 DELIMITER $$

Og dette etter utløserkommandoen din:

 DELIMITER;

Den enkle utløseren: Dataintegritet

Hvis du gjør selv den minste normalisering til databasestrukturen, har du sannsynligvis kjørt inn i en tid der du har slettet hoveddatakilden, men har fortsatt fragmenter som kjører rundt i datastrømmen din. For eksempel kan du ha en cart_id som refereres i to eller tre tabeller uten utenlandske nøkler, særlig siden utenlandske nøkler ikke støttes med MyISAM-motoren.

Det du sikkert har gjort tidligere er noe som dette (forenklet for illustrasjon):

 $ sql = 'DELETE FROM no_trigger_cart_items WHERE cart_id = 1'; $ rs = $ this-> db-> spørring ($ sql); $ sql = 'DELETE FROM no_trigger_carts WHERE cart_id = 1'; $ rs = $ this-> db-> spørring ($ sql);

Nå, avhengig av hvor godt du organiserer deg selv, kan det hende du har en enkelt API eller en metode som du vil rydde dine handlekurver. Hvis det er tilfelle, har du isolert logikken din for å kjøre disse to spørringene. Hvis det ikke er tilfelle, må du alltid huske å fjerne varekurvene dine når du sletter en bestemt handlevogn. Ikke vanskelig, men når du glemmer, mister du dataintegriteten din.

Skriv inn vår utløser. Jeg skal lage en veldig enkel utløser, slik at når jeg sletter en handlekurv, vil avtrekkeren avfyres for å slette noen vognobjekter som har samme cart_id:

 CREATE TRIGGER 'tutorial'. 'Before_delete_carts' FØR DELETE ON 'trigger_carts' FOR HVER RAD BEGIN DELETE FROM trigger_cart_items WHERE OLD.cart_id = cart_id; SLUTT

Veldig enkel syntaks som jeg sa ovenfor. La oss gå gjennom hver linje.

Min første linje sier "CREATE TRIGGER 'tutorial'. 'Before_delete_carts'". Jeg forteller MySQL for å lage en utløser på databasen "opplæring" for å få et navn på "before_delete_carts". Jeg pleier å nevne utløserne mine med formelen "When_How_Table". Det fungerer for meg, men det er mange andre måter å gjøre dette på.

Min andre linje forteller MySQL definisjonen av denne utløseren, "FØR SLETT PÅ" trigger_carts "FOR HVER RAD". Jeg forteller MySQL at før du sletter på dette bordet, for hver rad gjør noe. At noe er forklart neste, i BEGIN og SLUT. "SLETT FRA trigger_cart_items WHERE OLD.cart_id = cart_id;" Jeg forteller MySQL før du sletter fra trigger_carts, ta OLD.cart_id og slett også fra trigger_cart_items. Den gamle syntaksen er den definerte variabelen. Vi vil diskutere dette i neste avsnitt der vi kombinerer OLD og NEW.

Det er egentlig ingenting å skape denne utløseren. Fordelen er å flytte dataintegritetslogikken til datalaget ditt, som jeg kunne gjøre saken, er hvor den tilhører. Det er også en annen liten fordel, og det er den lave ytelsesgevinsten, sett nedenfor.

To spørringer:

En spørring med en utløser:

Som du kan se er det en liten ytelse gevinst, som bør forventes. Min database som jeg bruker, er på localhost med serveren min, men hvis jeg hadde brukt en egen DB-server, ville resultatforbedringen min bli litt større på grunn av rundturstid mellom de to serverne. Utløser sletningen min har litt høyere tid å slette, men det er bare ett spørsmål, så den totale tiden minker. Multipliser dette over all koden du bruker for å opprettholde dataintegriteten din, og ytelsesgevinsten blir minst beskjeden.

Ett notat på ytelsen, første gang utløseren går, kan det være mye tregere enn påfølgende tider. Jeg bruker ikke utløsere nødvendigvis for ytelsesgevinsten, men heller å flytte data logikken til datalaget mitt, akkurat som du vil flytte presentasjonen fra oppslaget ditt til presentasjonslaget, ellers kjent som CSS.


Den ganske enkle utløseren: logging og revisjon

Det neste eksemplet som vi vil se på, vil behandle logging. Si at jeg vil holde oversikt over hvert element som er plassert i en vogn. Kanskje, jeg vil overvåke kjøpesummen min. Kanskje, jeg vil bare ha en kopi av hvert element plassert i en vogn, ikke nødvendigvis solgt, bare for litt innsikt i tankene til kundene mine. Kanskje du har opprettet dine vognobjekter som et MINOR-bord, og du vil logge alle elementer i et InnoDB-bord. Uansett årsaken, la oss se på en INSERT-utløser, noe som åpner noen gode muligheter for logging eller revisjon av våre data.

Før triggere gjorde vi sannsynligvis noe som dette (igjen, forenklet for illustrasjon):

Nå kan vi lage en veldig enkel utløser for denne loggingsprosessen:

 CREATE TRIGGER 'after_insert_cart_items' AFTER INSERT ON 'trigger_cart_items' FOR HVER RAD BEGIN INSERT IN trigger_cart_log (cart_id, item_id) VALUES (NEW.cart_id, NEW.item_id); SLUTT

La oss løpe gjennom dette igjen, bare så det er klarhet om hva denne utløseren gjør. Først begynner vi med linjen, "CREATE TRIGGER 'after_insert_cart_items'". Jeg forteller igjen MySQL for å lage en utløser med navnet "after_insert_cart_items". Navnet kan være "Foo" eller "BullWinkle" eller hva du vil kalle det, men igjen, jeg foretrekker å illustrere mine utløsernavn. Deretter ser vi, "Etter at du trykker på 'trigger_cart_items' for hver rekke". Igjen sier dette etter at vi har lagt inn noe på trigger_cart_items, for hver rad er satt inn, utfør hva som er mellom BEGIN og END.

Til slutt har vi bare "INSERT IN trigger_cart_log (cart_id, item_id) VALUES (NEW.cart_id, NEW.item_id);" som er en standard spørring med unntak av mine to verdier. Jeg bruker den nye verdien som legges inn i cart_items tabellen.

Og vi har kuttet våre spørsmål i halv med den subtile prestasjonsøkningen:

Og bare for å sjekke at utløseren virker, ser jeg verdiene i tabellen min:

Dette er igjen, relativt enkelt, men vi jobber med et par verdier, noe som kan legge til kompleksiteten bare litt. La oss se på noe litt vanskeligere.


Den hardere utløseren: Business Logic

På dette punktet kan vi hoppe over den gamle måten å søke flere spørsmål med en enkelt spørring. Jeg kan tenke meg at det vil bli litt litt kjedelig å fortsette å måle resultatene av spørringer. I stedet la vi komme inn på noen få flere forhåndseksempler på utløsere.

Forretningslogikk er hvor feilene alltid kryper opp. Uansett, hvor forsiktig eller organisert vi er, slipper noe alltid gjennom sprekkene. Utløsere på UPDATE redusere det bare litt. Vi har litt strøm i en utløser for å evaluere hva den gamle verdien var, og sett den nye verdien basert på evalueringen. Si for eksempel at vi alltid vil ha vår pris på varer for å være en 30% oppsummering av prisen på varene. Det er naturlig å si at når vi oppdaterer vår pris, må vi også oppdatere vår pris. La oss håndtere det med en utløser.

 CREATE TRIGGER 'after_update_cost' Etter oppdatere på 'trigger_items_cost' FOR HVER RAD BEGIN UPDATE trigger_items SET price = (NEW.cost * 1,3) HVOR item_id = NEW.item_id; SLUTT

Det vi gjør er å oppdatere varetabellen med en pris basert på NEW.cost times 1.3. Jeg har inngått en pris på $ 50, så min nye pris skal være $ 65.

Sikkert nok virket denne utløseren også.

Vi må ta en titt på et litt mer avansert eksempel. Vi har allerede regelen om å endre prisen på en vare basert på kostnaden. La oss si at vi vil tiere våre kostnader litt. Hvis kostnaden er mindre enn $ 50, er prisen faktisk $ 50. Hvis det koster over $ 50, men mindre enn $ 100, blir vår pris $ 100 dollar. Selv om eksemplet mitt sannsynligvis ikke samsvarer med en sann forretningsregel, tilpasser vi kostnadene basert på faktorer hver dag. Jeg prøver bare å holde eksemplet lett å forstå.

For å gjøre dette, skal vi igjen jobbe med en UPDATE, men denne gangen vil vi brenne den før vi utfører spørringen. Vi skal også jobbe med en IF-erklæring, som er tilgjengelig for oss.

Her er den nye utløseren:

 CREATE TRIGGER 'before_update_cost' FØR OPPDATERING PÅ 'trigger_items_cost' FOR HVER RAD BEGINN NYHET. < 50 THEN SET NEW.cost = 50; ELSEIF NEW.cost > 50 og new.cost < 100 THEN SET NEW.cost = 100; END IF; END

Det vi gjør nå, ringer ikke på et spørsmål, men rettere bare overstyrer verdien. Jeg sier om kostnaden er mindre enn $ 50, så gjør det bare $ 50. Hvis kostnaden er mellom $ 50 og $ 100, så gjør den $ 100. Hvis det er over det, så la jeg det bare være det samme. Min syntaks her er ikke det utenlandske fra hvilket som helst annet sidespråk. Vi trenger å lukke vår IF-klausul med en END IF; men annet enn det, det er egentlig ikke vanskelig.

Bare for å se om utløseren virker, har jeg oppgitt en verdi på $ 30 for kostnaden, og den skal være $ 50:

Når jeg legger inn en kostnad på $ 85, her er verdien:

Og bare for å sjekke om min OPTER-utløseren fortsatt jobber, bør prisen min nå være $ 130:

Livet er godt.


Konklusjon

Jeg har bare rørt toppen av isbreen med utløsere og MySQL. Selv om det er utallige bruksområder for utløsere, har jeg fått det bra uten at de har håndtert dataene mine i logikklaget. Når det er sagt, er muligheten til å legge til regler til dataene mine i datalaget bare fornuftig. Når du legger til i de beskjedne ytelsesforbedringene, er fordelen enda større.

Vi må håndtere kompliserte webapplikasjoner med høy trafikk nå. Mens du bruker en utløser på en enkelt side, kan det ikke være best mulig bruk av tid og energi; en utløser på en kompleks webapplikasjon kan gjøre verden av forskjell. Jeg håper du likte eksemplene, og vennligst gi meg beskjed om hva som trenger videre forklaring.