{"id":646,"date":"2005-11-01T13:43:52","date_gmt":"2005-11-01T11:43:52","guid":{"rendered":"http:\/\/olbertz\/2005\/11\/01\/mysql-5-trigger-sind-da\/"},"modified":"2013-01-20T14:35:33","modified_gmt":"2013-01-20T13:35:33","slug":"mysql-5-trigger-sind-da","status":"publish","type":"post","link":"https:\/\/olbertz.de\/blog\/2005\/11\/01\/mysql-5-trigger-sind-da\/","title":{"rendered":"MySQL 5: Trigger sind da!"},"content":{"rendered":"<p>F&#252;r den <a href=\"http:\/\/www.olbertz.de\/archives\/000637.html\">Counterkram<\/a> habe ich die Datenbank gestern auf <a href=\"http:\/\/www.mysql.com\/\">MySQL 5<\/a> aktualisiert. Der wichtigste Grund war die Einf&#252;hrung von <a href=\"http:\/\/dev.mysql.com\/tech-resources\/articles\/mysql-triggers.html\">Triggern<\/a>. Mit diesem (bei &#8222;richtigen&#8220; Datenbanken schon lange vorhandenen) Feature ist es m&#246;glich, innerhalb der Datenbank direkt auf <tt>SELECT<\/tt>, <tt>UPDATE<\/tt> oder <tt>INSERT<\/tt> einer Tabell zu reagieren und weitere Aktionen anzusto&#223;en.<\/p>\n<p>Momentan halte ich die Daten f&#252;r st&#252;ndliche, t&#228;gliche und monatliche Zugriffe auf die einzelnen Blogs in separate Tabellen der gleichen Datenbank fest. Nat&#252;rlich k&#246;nnte man auch mit nur einer Tabelle auskommen, n&#228;mlich einer, in der die Zugriffe protokolliert werden und mit entsprechenden <tt>SUM<\/tt>&#8211; und <tt>GROUP BY<\/tt>-Spielereien die gew&#252;nschten Daten aus dieser einen Tabelle auswerten.<\/p>\n<p>Diese Tabelle wird aber ziemlich schnell sehr gro&#223; und damit auch tr&#228;ge, wenn man nicht gerade viel Geld in teure Hardware steckt. Um dieses Problem zu l&#246;sen, habe ich mich also f&#252;r die redundante Speicherung der Daten entschieden. Bevor ich auf MySQL 5 umgestiegen bin, musste ich dies aber auf Applikationsebene machen, was mir nicht besonders gef&#228;llt, da es (a) genau f&#252;r solche F&#228;lle Trigger gibt und (b) die Daten einfach konsistenter sind, wenn die Datenbank sich um die redundante Speicherung k&#252;mmert.<\/p>\n<p>Nun kommen also die Trigger ins Spiel. Daf&#252;r nehmen wir ein einfaches Beispiel an: In den Logfiles ist der <a href=\"http:\/\/de.wikipedia.org\/wiki\/User-Agent\">User-Agent<\/a> schon so aufbereitet, dass nur noch &#8222;Internet Explorer&#8220;, &#8222;<a href=\"http:\/\/www.apple.com\/macosx\/features\/safari\/\">Safari<\/a>&#8222;, etc. als Browser angegeben ist. In einer separaten Tabelle soll nun pro Blog eine Statistik der verwendeten Browser abgelegt werden. Dabei interessiert nur die monatliche Verteilung. Die Tabelle <i>tbl_browser_stat<\/i> hat also die drei Felder <i>blog_id<\/i>, <i>month<\/i>, <i>browser<\/i> und <i>impressions<\/i>. <\/p>\n<p>&#220;ber einen Trigger soll nun bei jeder neuen Zeile in der Tabelle mit den Logfile-Zeilen, die Tabelle <i>tbl_browser_stat<\/i> aktualisiert werden. Und hier kommt jetzt ein weiteres, kleines Problem ins Spiel: Falls f&#252;r die Kombination aus Browser und Blog noch kein Eintrag in der Datenbank vorhanden ist, muss ja ein <tt>INSERT<\/tt> statt eines <tt>UPDATE<\/tt>s gemacht werden. Daf&#252;r gibt es in MySQL das Konstrukt <tt>INSERT ... ON DUPLICATE KEY UPDATE ...<\/tt>. Deer Trigger sieht dann also folgenderma&#223;en aus:<\/p>\n<pre>\r\nCREATE TRIGGER monthly_browser_stats AFTER INSERT ON tbl_log_rows\r\n    FOR EACH ROW\r\n        INSERT INTO tbl_browser_stats SET blog_id=NEW.blog_id, month=NEW.month, browser=NEW.browser, impressions=1 \r\n        ON DUPLICATE KEY UPDATE impressions=impressions+1;\r\n<\/pre>\n<p>Auf der Tabelle <i>tbl_browser_stats<\/i> wurde ein <tt>UNIQUE<\/tt>-Key auf die Kombination von <i>blog_id<\/i>, <i>month<\/i> und <i>browser<\/i> angelegt. Von nun an wird die Tabelle automatisch gef&#252;llt und es k&#246;nnen sehr einfach die Verteilung der Zugriffe bestimmter Browser auf einzelne oder alle Blogs abgefragt werden. Die Applikation wird von mir in den n&#228;chsten Tagen schrittweise von diesem Ballast befreit, neue Statistiken k&#246;nnen dann fast ausschlie&#223;lich &#252;ber neue Trigger realisiert werden.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>F&#252;r den Counterkram habe ich die Datenbank gestern auf MySQL 5 aktualisiert. Der wichtigste Grund war die Einf&#252;hrung von Triggern. Mit diesem (bei &#8222;richtigen&#8220; Datenbanken schon lange vorhandenen) Feature ist es m&#246;glich, innerhalb der Datenbank direkt auf SELECT, UPDATE oder &hellip; <a href=\"https:\/\/olbertz.de\/blog\/2005\/11\/01\/mysql-5-trigger-sind-da\/\">Weiterlesen <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[74],"tags":[],"class_list":["post-646","post","type-post","status-publish","format-standard","hentry","category-technik"],"_links":{"self":[{"href":"https:\/\/olbertz.de\/blog\/wp-json\/wp\/v2\/posts\/646","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/olbertz.de\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/olbertz.de\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/olbertz.de\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/olbertz.de\/blog\/wp-json\/wp\/v2\/comments?post=646"}],"version-history":[{"count":1,"href":"https:\/\/olbertz.de\/blog\/wp-json\/wp\/v2\/posts\/646\/revisions"}],"predecessor-version":[{"id":2013,"href":"https:\/\/olbertz.de\/blog\/wp-json\/wp\/v2\/posts\/646\/revisions\/2013"}],"wp:attachment":[{"href":"https:\/\/olbertz.de\/blog\/wp-json\/wp\/v2\/media?parent=646"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/olbertz.de\/blog\/wp-json\/wp\/v2\/categories?post=646"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/olbertz.de\/blog\/wp-json\/wp\/v2\/tags?post=646"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}