Einführung in MySQL-Trigger

Wahrscheinlich wissen Sie, was ein Datenbank-Trigger ist, zumindest in konzeptioneller Hinsicht. Die Chancen sind noch größer, dass Sie wissen, dass MySQL Trigger unterstützt und diese seit geraumer Zeit unterstützt. Ich würde vermuten, sogar mit diesem Wissen bewaffnet, dass viele von Ihnen die Auslöser von MySQL nicht nutzen. Sie gehören zu den Dingen, die unbedingt in Ihrer Entwicklungs-Toolbox enthalten sein sollten, da sie die Art und Weise, wie Sie Ihre Daten betrachten, wirklich verändern können.


Einführung: Was ist ein Auslöser?

"Je mehr Anwendungen jedoch immer komplizierter werden, je weiter wir die Schichten einer Anwendung so abstrahieren können, wie sie sollen, umso besser, wie sie sollen, desto besser wird unsere interne Entwicklungsfähigkeit."

Für Uneingeweihte ist ein Auslöser eine Regel, die Sie auf eine Tabelle setzen, die im Wesentlichen besagt, dass Sie immer etwas anderes tun, wenn Sie etwas LÖSCHEN, AKTUALISIEREN oder EINFÜGEN in dieser Tabelle einfügen. Beispielsweise möchten wir vielleicht eine Änderung protokollieren. Statt jedoch zwei separate Abfragen zu schreiben, eine für die Änderung und eine für das Protokoll, können wir stattdessen einen Trigger schreiben, der besagt: "Wenn diese Zeile aktualisiert wird, erstellen Sie eine neue Zeile in einer anderen Tabelle zu sagen, dass das Update gemacht wurde ". Dadurch wird der ersten Abfrage etwas mehr Aufwand hinzugefügt. Da jedoch nicht zwei Pakete in Ihre Datenbank übertragen werden, um zwei separate Aufgaben auszuführen, ergibt sich eine allgemeine Leistungssteigerung (theoretisch sowieso)..

In Version 5.0.2 wurden Auslöser in MySQL eingeführt. Die Syntax für einen Trigger ist beim ersten Erröten etwas fremd. MySQL verwendet den ANSI SQL: 2003-Standard für Prozeduren und andere Funktionen. Wenn Sie mit einer Programmiersprache im Allgemeinen vertraut sind, ist es nicht so schwer zu verstehen. Die Spezifikation ist nicht frei verfügbar, daher werde ich mein Bestes tun, um einfache Strukturen zu verwenden und zu erklären, was im Trigger geschieht. Sie werden sich mit den gleichen logischen Strukturen befassen, die jede Programmiersprache bietet.

Wie ich oben erwähnt habe, werden Trigger prozessual bei UPDATE-, DELETE- und INSERT-Ereignissen ausgeführt. Was ich nicht erwähnt habe, ist, dass sie entweder vor oder nach dem definierten Ereignis ausgeführt werden können. Daher könnten Sie einen Auslöser haben, der vor einem DELETE-Befehl oder nach einem DELETE-Befehl usw. ausgelöst wird. Dies bedeutet, dass Sie einen Auslöser haben können, der vor einem INSERT ausgelöst wird, und einen separaten, der nach einem INSERT ausgelöst wird, was sehr mächtig sein kann.

Ich werde drei Anwendungsbereiche betrachten, die Sie Ihrer Toolbox hinzufügen könnten. Es gibt mehrere Verwendungsmöglichkeiten, auf die ich mich nicht einlassen möchte, da ich der Meinung bin, dass es bessere Methoden gibt, um dieselben Ergebnisse zu erzielen, oder dass sie ein eigenes Tutorial verdienen. Jede dieser Anwendungen, die ich erforsche, hat ein Gegenstück in Ihrer serverseitigen Logikschicht und ist keine neuen Konzepte. Je mehr Anwendungen jedoch immer komplizierter werden, je weiter wir die Schichten einer Anwendung so abstrahieren können, dass sie mit dem arbeiten, was sie sollen, desto größer wird unsere interne Entwicklungsbenutzbarkeit.


Anfänge: Meine Tabellenstruktur, Tools und Notizen

Ich arbeite mit einem mythischen Kartensystem, mit Artikeln, die Preise haben. Ich habe versucht, die Datenstruktur nur zur Veranschaulichung so einfach wie möglich zu halten. Ich benenne Spalten und Tabellen zum Zweck des Verständnisses und nicht zur Verwendung in der Produktion. Ich benutze auch TIMESTAMPS anstelle von anderen Alternativen. Für diejenigen, die die Heimversion des heutigen Spiels spielen, verwende ich die Tabellennamen der Karren, cart_items, cart_log, items, items_cost.

Bitte beachten Sie, dass ich in diesem Tutorial sehr einfache Abfragen verwenden werde, um meine Punkte auszudrücken. Ich bin an keine Variablen gebunden, da ich keine Benutzereingaben verwende. Ich möchte, dass die Abfragen so einfach wie möglich zu lesen sind, aber dieses Tutorial sollte nur für praktische Triggeranwendungen verwendet werden. Ich weiß, es könnte ein oder zwei Kommentare dazu geben, also betrachten Sie meinen Haftungsausschluss.

Ich verwende den Particle Tree PHP Quick Profiler, um die Ausführungszeiten zu sehen. Ich verwende auch die im Tool enthaltene Datenbank-Abstraktionsschicht nur zu meinem eigenen Vorteil. Es ist ein schönes Werkzeug und bietet weit mehr als nur die SQL-Ausführungszeiten.

Ich verwende auch Chive, um die DB-Effekte zu veranschaulichen und meine Trigger zu erstellen. Chive ist nur MySQL 5+ und ist PHPMyAdmin sehr ähnlich. Es ist hübscher, aber im Moment auch viel fehlerhafter. Ich verwende Chive, einfach weil es gute Screenshots gibt, was mit den Abfragen passiert.

Noch eine kurze Notiz. Sie müssen möglicherweise das Trennzeichen für MySQL ändern, während Sie einen Trigger erstellen. Das natürliche Trennzeichen für MySQL ist: Da wir dieses Trennzeichen jedoch für unsere hinzugefügten Abfragen verwenden werden, müssen Sie das Trennzeichen möglicherweise explizit umbenennen, wenn Sie diese über die Befehlszeile erstellen. Ich habe mich entschieden, dies nicht zu zeigen, da bei Verwendung von Schnittlauch der Trenner nicht geändert werden muss.

Um ein Trennzeichen zu ändern, führen Sie dies einfach vor Ihrem Auslösebefehl aus:

 DELIMITER $$

Und das nach deinem Auslösebefehl:

 DELIMITER;

Der einfache Auslöser: Datenintegrität

Wenn Sie selbst die geringste Normalisierung Ihrer Datenbankstruktur vornehmen, haben Sie wahrscheinlich eine Zeit überschritten, in der Sie die Hauptdatenquelle gelöscht haben, aber in Ihrem Datenstrom noch Fragmente ausgeführt werden. Beispielsweise haben Sie eine cart_id, auf die in zwei oder drei Tabellen ohne Fremdschlüssel verwiesen wird, insbesondere da Fremdschlüssel nicht mit der MyISAM-Engine unterstützt werden.

Was Sie wahrscheinlich in der Vergangenheit gemacht haben, ist etwa so (vereinfacht für die Darstellung):

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

Je nachdem, wie gut Sie sich selbst organisieren, verfügen Sie möglicherweise über eine einzige API oder Methode, mit der Sie Ihre Einkaufswagen löschen können. Wenn dies der Fall ist, haben Sie Ihre Logik isoliert, um diese beiden Abfragen auszuführen. Wenn dies nicht der Fall ist, müssen Sie immer daran denken, Ihre Warenkorbartikel zu löschen, wenn Sie einen bestimmten Warenkorb löschen. Nicht schwierig, aber wenn Sie es vergessen, verlieren Sie Ihre Datenintegrität.

Betritt unseren Abzug. Ich werde einen sehr einfachen Auslöser erstellen, sodass jedes Mal, wenn ich einen Einkaufswagen lösche, der Auslöser ausgelöst wird, um alle Artikel des Wagens zu löschen, die dieselbe cart_id haben:

 TRIGGER 'Tutorial' erstellen. 'Before_delete_carts' BEFORE DELETE ON 'trigger_carts' FÜR JEDE ROW BEGIN DELETE FROM trigger_cart_items WHERE OLD.cart_id = cart_id; ENDE

Sehr einfache Syntax, wie ich oben gesagt habe. Lass uns durch jede Zeile gehen.

Meine erste Zeile enthält "CREATE TRIGGER 'Tutorial'. 'Before_delete_carts'". Ich sage MySQL, einen Auslöser für die Datenbank "Tutorial" mit dem Namen "before_delete_carts" zu erstellen. Ich neige dazu, meine Trigger mit der Formel "When_How_Table" zu benennen. Das funktioniert für mich, aber es gibt viele andere Möglichkeiten, dies zu tun.

Meine zweite Zeile teilt MySQL mit, wie dieser Auslöser definiert ist: "VOR DELETE ON 'trigger_carts' FÜR JEDE REIHE". Ich sage MySQL, dass Sie für jede Zeile etwas tun, bevor Sie diese Tabelle löschen. Als Nächstes wird in BEGIN und END etwas erklärt. "DELETE FROM trigger_cart_items WHERE OLD.cart_id = cart_id;" Ich sage MySQL, bevor Sie aus trigger_carts löschen, die OLD.cart_id nehmen und auch aus trigger_cart_items löschen. Die ALTE Syntax ist die definierte Variable. Wir werden das im nächsten Abschnitt besprechen, wo wir ALT und NEU kombinieren werden.

Es gibt wirklich nichts, um diesen Trigger zu erstellen. Der Vorteil besteht darin, Ihre Datenintegritätslogik auf Ihre Datenschicht zu verschieben, wobei ich den Fall machen könnte, wo sie hingehört. Es gibt noch einen weiteren leichten Vorteil, und das ist der leichte Leistungszuwachs (siehe unten).

Zwei Abfragen:

Eine Abfrage mit einem Trigger:

Wie Sie sehen, gibt es einen leichten Leistungszuwachs, der zu erwarten ist. Meine Datenbank, die ich verwende, befindet sich auf localhost mit meinem Server, aber wenn ich einen separaten DB-Server verwendet hätte, wäre mein Leistungsgewinn aufgrund der Roundtrip-Zeit zwischen den beiden Servern etwas größer. Mein Trigger-Delete hat etwas mehr Zeit zum Löschen, aber es gibt nur eine Abfrage, sodass die Gesamtzeit abnimmt. Multiplizieren Sie dies mit dem gesamten Code, den Sie zur Wahrung Ihrer Datenintegrität verwenden, und der Leistungsgewinn wird zumindest mäßig.

Eine Anmerkung zur Leistung: Wenn der Auslöser zum ersten Mal ausgeführt wird, ist er möglicherweise viel langsamer als die folgenden Zeiten. Ich benutze Trigger nicht notwendigerweise für die Leistungssteigerung, sondern um meine Datenlogik auf meine Datenschicht zu verschieben, genau wie Sie Ihre Präsentation von Ihrem Markup auf Ihre Präsentationsebene verschieben möchten (auch als CSS bezeichnet).


Der ziemlich einfache Auslöser: Protokollierung und Überwachung

Das nächste Beispiel, das wir uns ansehen werden, befasst sich mit der Protokollierung. Angenommen, ich möchte jeden Artikel in einem Einkaufswagen nachverfolgen. Vielleicht möchte ich die Einkaufspreise für Ihre Warenkorbartikel überwachen. Vielleicht möchte ich nur eine Kopie jedes Artikels in einen Einkaufswagen legen, der nicht unbedingt verkauft wird, nur um einen Einblick in die Gedanken meiner Kunden zu erhalten. Möglicherweise haben Sie Ihre Warenkorbartikel als MEMORY-Tabelle erstellt und möchten alle Artikel in einer InnoDB-Tabelle protokollieren. Was auch immer der Grund sein mag, schauen wir uns einen INSERT-Trigger an, der einige gute Möglichkeiten für die Protokollierung oder Überprüfung unserer Daten eröffnet.

Vor dem Auslösen haben wir wahrscheinlich so etwas (wieder vereinfacht zur Veranschaulichung) gemacht:

Jetzt können wir einen sehr einfachen Auslöser für diesen Protokollierungsprozess erstellen:

 CREATE TRIGGER 'after_insert_cart_items' AFTER INSERT ON 'trigger_cart_items' FÜR JEDE ZEIT BEGINN INSERT in_ trigger_cart_log (cart_id, item_id) VALUES (NEW.cart_id, NEW.item_id); ENDE

Lassen Sie uns das noch einmal durchgehen, nur damit klar ist, was dieser Auslöser tut. Zuerst beginnen wir mit der Zeile "CREATE TRIGGER 'after_insert_cart_items'". Ich sage MySQL erneut, einen Trigger mit dem Namen "after_insert_cart_items" zu erstellen. Der Name könnte "Foo" oder "BullWinkle" oder wie auch immer Sie es nennen wollen, aber ich möchte lieber meine Triggernamen veranschaulichen. Als nächstes sehen wir "NACH EINFÜGEN AUF 'trigger_cart_items' FÜR JEDE REIHE". Das heißt wieder, nachdem wir etwas in trigger_cart_items eingefügt haben, führen Sie für jede eingefügte Zeile aus, was sich zwischen meinem BEGIN und END befindet.

Schließlich haben wir nur "INSERT INTO trigger_cart_log (cart_id, item_id) VALUES (NEW.cart_id, NEW.item_id);" Dies ist eine Standardabfrage mit Ausnahme meiner beiden Werte. Ich verwende den Wert NEW, der in die Tabelle cart_items eingefügt wird.

Und mit dem subtilen Leistungsgewinn haben wir unsere Anfragen halbiert:

Und nur um zu überprüfen, ob unser Auslöser funktioniert, sehe ich die Werte in meiner Tabelle:

Das ist wieder relativ einfach, aber wir arbeiten mit ein paar Werten, was die Komplexität nur ein wenig erhöhen kann. Schauen wir uns etwas etwas härter an.


Der härtere Auslöser: Geschäftslogik

An dieser Stelle können wir den alten Weg mehrerer Abfragen mit einer einzigen Abfrage überspringen. Ich kann mir vorstellen, dass dies nur ein wenig langweilig werden wird, um die Leistung von Abfragen weiterhin zu messen. Lassen Sie uns stattdessen auf einige weitere fortgeschrittene Beispiele für Auslöser eingehen.

In der Geschäftslogik schleichen sich immer die Fehler ein. Unabhängig davon, wie vorsichtig oder organisiert wir sind, rutscht immer etwas durch die Ritzen. Trigger bei UPDATE mildern das nur ein bisschen. Wir verfügen über einen Trigger, um den ALTEN Wert zu bewerten und den NEUEN Wert basierend auf der Bewertung festzulegen. Angenommen, wir möchten, dass unser Preis für Artikel immer einen Preisaufschlag von 30% der Artikelkosten ausmacht. Es ist natürlich sinnvoll, wenn wir unsere Kosten AKTUALISIEREN, müssen wir auch unseren Preis AKTUALISIEREN. Lassen Sie uns das mit einem Auslöser behandeln.

 CREATE TRIGGER 'after_update_cost' NACH UPDATE ON 'trigger_items_cost' FÜR JEDE ROW BEGIN UPDATE trigger_items SET Preis = (NEW.cost * 1.3) WHERE item_id = NEW.item_id; ENDE

Was wir tun, ist die Aktualisierung der Artikeltabelle mit einem Preis basierend auf den NEW.cost-Zeiten 1.3. Ich habe einen Preis von 50 USD eingegeben, mein neuer Preis sollte also 65 USD betragen.

Dieser Auslöser hat natürlich auch funktioniert.

Wir müssen uns ein etwas fortgeschritteneres Beispiel ansehen. Wir haben bereits die Regel, den Preis eines Artikels basierend auf dessen Kosten zu ändern. Nehmen wir an, wir wollen unsere Kosten ein wenig stufen. Wenn die Kosten weniger als 50 US-Dollar betragen, betragen unsere Kosten tatsächlich 50 US-Dollar. Wenn die Kosten über 50 US-Dollar, aber unter 100 US-Dollar liegen, betragen unsere Kosten 100 US-Dollar. Während mein Beispiel wahrscheinlich keiner echten Geschäftsregel entspricht, passen wir die Kosten täglich an. Ich versuche lediglich, das Beispiel verständlich zu halten.

Um dies zu tun, werden wir wieder mit einem UPDATE arbeiten, aber dieses Mal werden wir es abfeuern, bevor wir unsere Abfrage ausführen. Wir werden auch mit einer IF-Anweisung arbeiten, die uns zur Verfügung steht.

Hier ist der neue Auslöser:

 CREATE TRIGGER 'before_update_cost' VOR UPDATE ON 'trigger_items_cost' FÜR JEDE ZEIT BEGINN WENN NEUE.KOSTEN < 50 THEN SET NEW.cost = 50; ELSEIF NEW.cost > 50 und neu.kosten < 100 THEN SET NEW.cost = 100; END IF; END

Was wir jetzt tun, ruft keine Abfrage auf, sondern überschreibt nur den Wert. Ich sage, wenn die Kosten weniger als 50 US-Dollar betragen, dann verdienen Sie einfach 50 US-Dollar. Wenn die Kosten zwischen 50 und 100 US-Dollar liegen, verdienen Sie 100 US-Dollar. Wenn es darüber liegt, lass ich es einfach gleich bleiben. Meine Syntax hier ist von keiner anderen serverseitigen Sprache so fremd. Wir müssen unsere IF-Klausel mit einem END-IF abschließen. Aber abgesehen davon ist es wirklich nicht schwierig.

Um zu überprüfen, ob unser Auslöser funktioniert, habe ich einen Wert von 30 US-Dollar für die Kosten eingegeben, und er sollte 50 US-Dollar betragen:

Wenn ich Kosten von $ 85 eingebe, ist hier der Wert:

Um zu überprüfen, ob mein AFTER UPDATE-Auslöser noch funktioniert, sollte mein Preis jetzt 130 US-Dollar betragen:

Das leben ist gut.


Fazit

Ich habe die Spitze des Eisbergs nur mit Auslösern und MySQL berührt. Zwar gibt es unzählige Verwendungsmöglichkeiten für Auslöser, aber ich bin in der Vergangenheit ohne sie gut ausgekommen, indem ich mich mit meinen Daten in meiner Logikschicht beschäftige. Die Möglichkeit, Regeln zu meinen Daten in der Datenschicht hinzuzufügen, ist jedoch sinnvoll. Wenn Sie die bescheidenen Leistungsverbesserungen hinzufügen, ist der Vorteil noch größer.

Wir müssen uns jetzt mit komplizierten Webanwendungen mit hohem Traffic beschäftigen. Bei Verwendung eines Auslösers auf einer einzelnen Seite ist Vanity-Site möglicherweise nicht die beste Verwendung von Zeit und Energie. Ein Auslöser für eine komplexe Webanwendung kann den Unterschied ausmachen. Ich hoffe, dass Ihnen die Beispiele gefallen haben, und lassen Sie mich wissen, was weiterer Erklärung bedarf.