Deaktivieren Sie den Trigger für nur einen Tisch
Frage
Ist es möglich, einen Auslöser momentan zu deaktivieren, aber nur für einen Tisch?
Zum Beispiel habe ich Tabelle, Tablea mit einem Einfügen, Aktualisieren und Löschen von Trigger. Ich habe auch eine Tabelle B mit den gleichen Auslösern, aber sie beeinflussen nur bestimmte Spalten in Tabelle A.
Ich habe jetzt eine Update -Abfrage, die beide Tabellen verwendet. Ich kenne die Updates in Tabelle, um die Auslöser abzufeuern, aber die Updates in Tabelle B müssen die Trigger definitiv nicht abfeuern. Deshalb möchte ich diese Auslöser deaktivieren, bis die Updates abgeschlossen sind.
Ist das möglich? Ich benutze MySQL 5.1
NACHTRAG
Hier ist eine Triggertabelle a im Wesentlichen
BEGIN
IF (OLD.status != 1 AND NEW.status = 2) THEN
IF (OLD.geo_lat IS NOT NULL AND OLD.geo_long IS NOT NULL) THEN
DELETE FROM geo WHERE datatype IN (3,4) AND foreignid = NEW.id;
END IF;
ELSEIF (OLD.Status = 1 AND NEW.Status != 2) THEN
IF (NEW.geo_lat IS NOT NULL AND NEW.geo_long IS NOT NULL) THEN
INSERT INTO geo (datatype, foreignid, long, lat, hostid, morton, status) VALUES (IF(NEW.groupType=1,3,4), NEW.id, NEW.geo_long, NEW.geo_lat, NEW.hostid, 0, NEW.Status);
END IF;
ELSEIF (NEW.status != 3) THEN
IF (OLD.geo_lat IS NOT NULL AND OLD.geo_long IS NOT NULL AND (NEW.geo_lat IS NULL OR NEW.geo_long IS NULL)) THEN
DELETE FROM geo WHERE datatype IN (3,4) AND foreignid = NEW.id;
ElSEIF ((OLD.geo_lat IS NULL OR OLD.geo_long IS NULL) AND NEW.geo_lat IS NOT NULL AND NEW.geo_long IS NOT NULL) THEN
INSERT INTO geo (datatype, foreignid, longitude, latitude, hostid, morton, status) VALUES (IF(NEW.groupType=1,3,4), NEW.id, NEW.geo_long, NEW.geo_lat, NEW.hostid, 0, NEW.Status);
ELSEIF (OLD.geo_lat!=NEW.geo_lat OR OLD.geo_long != NEW.geo_long OR OLD.status != NEW.status) THEN
UPDATE geo SET lat = NEW.geo_lat, long = NEW.geo_long, status = NEW.status WHERE datatype IN (3,4) AND foreignid = NEW.id;
END IF;
END IF;
END
Hier sind die Trigger in Tabelle B im Wesentlichen
CREATE TRIGGER `usergroups_comments_insert` AFTER INSERT ON `usergroups_comment`
FOR EACH ROW
BEGIN
CALL sp-set-comment_count(NEW.`gid`);
END;
Hier ist das gespeicherte Verfahren, das aus Tabelle B abgefeuert wird
DELIMITER $$
CREATE PROCEDURE `sp_set-comment_count` (IN _id INT)
BEGIN
-- AC - All Count
-- OLDAC- Old All Count
DECLARE AC, OLDAC INT DEFAULT 0;
SELECT COUNT(*) AS ac
INTO AC
FROM usergroups AS ug
LEFT JOIN usergroup_comments AS ugm ON ugm.`gid` = ug.`id`
LEFT JOIN mediagallery AS dm ON ugm.mid = dm.`id`
WHERE dm.`status` NOT IN (200, 201, 202, 203, 204, 205)
AND ug.`id` = _id;
SELECT allCount
INTO OLDAC
FROM usergroups
WHERE ug.`id` = _id;
IF (OLDAC <> AC) THEN
UPDATE usergroups
SET allCount = AC,
WHERE usergroups.`id` = _id;
END IF;
END $$
Lösung
Wenn Sie dann einen IF -Blockieren in jedem Auslöser einfügen, können Sie alle Auslöser effektiv herunterfahren. Hier ist ein solcher Codeblock
IF @TRIGGER_DISABLED = 0 THEN
...trigger body
END IF;
In der MySQL -Umgebung könnten Sie
- Lauf
SET @TRIGGER_DISABLED = 1;
- Machen Sie Ihre Datenwartung
- Lauf
SET @TRIGGER_DISABLED = 0;
Ihr Auslöser für Table A sollte also folgendermaßen aussehen:
BEGIN
IF @TRIGGER_DISABLED = 0 THEN
IF (OLD.status != 1 AND NEW.status = 2) THEN
IF (OLD.geo_lat IS NOT NULL AND OLD.geo_long IS NOT NULL) THEN
DELETE FROM geo WHERE datatype IN (3,4) AND foreignid = NEW.id;
END IF;
ELSEIF (OLD.Status = 1 AND NEW.Status != 2) THEN
IF (NEW.geo_lat IS NOT NULL AND NEW.geo_long IS NOT NULL) THEN
INSERT INTO geo (datatype, foreignid, long, lat, hostid, morton, status) VALUES (IF(NEW.groupType=1,3,4), NEW.id, NEW.geo_long, NEW.geo_lat, NEW.hostid, 0, NEW.Status);
END IF;
ELSEIF (NEW.status != 3) THEN
IF (OLD.geo_lat IS NOT NULL AND OLD.geo_long IS NOT NULL AND (NEW.geo_lat IS NULL OR NEW.geo_long IS NULL)) THEN
DELETE FROM geo WHERE datatype IN (3,4) AND foreignid = NEW.id;
ElSEIF ((OLD.geo_lat IS NULL OR OLD.geo_long IS NULL) AND NEW.geo_lat IS NOT NULL AND NEW.geo_long IS NOT NULL) THEN
INSERT INTO geo (datatype, foreignid, longitude, latitude, hostid, morton, status) VALUES (IF(NEW.groupType=1,3,4), NEW.id, NEW.geo_long, NEW.geo_lat, NEW.hostid, 0, NEW.Status);
ELSEIF (OLD.geo_lat!=NEW.geo_lat OR OLD.geo_long != NEW.geo_long OR OLD.status != NEW.status) THEN
UPDATE geo SET lat = NEW.geo_lat, long = NEW.geo_long, status = NEW.status WHERE datatype IN (3,4) AND foreignid = NEW.id;
END IF;
END IF;
END IF;
END
Ihr Auslöser für Tabelle B sollte also folgendermaßen aussehen:
CREATE TRIGGER `usergroups_comments_insert` AFTER INSERT ON `usergroups_comment`
FOR EACH ROW
BEGIN
IF @TRIGGER_DISABLED = 0 THEN
CALL sp-set-comment_count(NEW.`gid`);
END IF;
END;
Wenn Sie möchten, dass die Auslöser für Tabelle A, jedoch nicht in Tabelle B gestartet werden, fügen Sie den Codeblock nur dem Trigger von Tabelle B hinzu.
Andere Tipps
Sie können diesen Ansatz vereinfachen.
- Fügen Sie einfach ein Etikett zum ersten "Beginn" -Block hinzu.
- Test, wenn die Steuervariable nicht null ist.
- Wenn ja, hinterlassen Sie den Abzug.
Sie können es also vermeiden, den ursprünglichen Triggercode in eine "if" -Antatment zu wickeln. Nur der Triggerkopf muss genau definiert werden - was einfacher und viel zuverlässiger ist.
Beispiel:
CREATE TRIGGER [YOUR_TRIGGER_SPEC]
Trigger: BEGIN
IF @TRIGGER_DISABLED NOT NULL THEN
LEAVE Trigger;
END IF;
[YOUR CODE]
END;
Habe Spaß :-)
Bearbeiten von Rolandomysqldba 2012-05-01 18:38 EDT
Ich habe das vor Monaten versucht und es ist instabil. Hier ist, wie:
Beispieltabelle
mysql> show create table mytext\G
*************************** 1. row ***************************
Table: mytext
Create Table: CREATE TABLE `mytext` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`txt` text NOT NULL,
`txtmd5` char(32) NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
UNIQUE KEY `txtmd5` (`txtmd5`)
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=latin1
1 row in set (0.00 sec)
mysql> select * from mytext;
+----+------------------------+----------------------------------+
| id | txt | txtmd5 |
+----+------------------------+----------------------------------+
| 1 | Rolando Edwards | ab14209a029a8f6d42d7c5a5d7a77623 |
| 2 | Pamela Edwards | 5dcbd06ea48c690032b1b29a514eb0e2 |
| 3 | Dominique Edwards | 7487431d74ac2d17a9d63123672a4bdf |
| 4 | Diamond Edwards | bc8d80541a000ed506048134058e2878 |
| 5 | The Quick Brown Fox | be94284cfa534c1837e744c061f71e17 |
| 11 | Quick Brown Fox Jumped | 495a52136057b242a80e514d7cbe77c7 |
+----+------------------------+----------------------------------+
6 rows in set (0.00 sec)
mysql>
Hier ist der Auslöser:
DROP TRIGGER IF EXISTS mytext_bi;
DELIMITER $$
CREATE TRIGGER mytext_bi BEFORE INSERT ON mytext FOR EACH ROW
TheTrigger:BEGIN
DECLARE found_count INT;
SELECT COUNT(1) INTO found_count
FROM mytext WHERE txtmd5 = MD5(LEFT(new.txt,10));
IF found_count = 1 THEN
LEAVE TheTrigger;
END IF;
SET new.txtmd5 = MD5(LEFT(new.txt,10));
END $$
DELIMITER ;
Schauen Sie, was passiert, wo ich versuche, "Dominique Edwards" einzufügen
mysql> insert into mytext (txt) values ('Dominique Edwards');
Query OK, 1 row affected (0.06 sec)
mysql> select * from mytext;
+----+------------------------+----------------------------------+
| id | txt | txtmd5 |
+----+------------------------+----------------------------------+
| 1 | Rolando Edwards | ab14209a029a8f6d42d7c5a5d7a77623 |
| 2 | Pamela Edwards | 5dcbd06ea48c690032b1b29a514eb0e2 |
| 3 | Dominique Edwards | 7487431d74ac2d17a9d63123672a4bdf |
| 4 | Diamond Edwards | bc8d80541a000ed506048134058e2878 |
| 5 | The Quick Brown Fox | be94284cfa534c1837e744c061f71e17 |
| 11 | Quick Brown Fox Jumped | 495a52136057b242a80e514d7cbe77c7 |
| 14 | Dominique Edwards | |
+----+------------------------+----------------------------------+
7 rows in set (0.00 sec)
mysql>
Ahhh, es rutschte sowieso rein !!!
Ich werde Ihre Antwort noch nicht nur wegen Ihrer Anstrengungen, sondern auch, weil ich genau das Gleiche ausprobiert habe (ein Etikett in der Nähe zu setzen BEGIN
) Und es hat damals bei mir nicht funktioniert. Was für ein Weg zu lernen.
Niemand sollte den mySQL gespeicherten Verfahrenssprache voll und ganz vertrauen (die Signalverarbeitung wird nicht ordnungsgemäß implementiert), und Sie betonten, als Sie "Spaß haben" sagten, wenn Sie "Spaß haben". Manchmal bedeutet es, Spaß zu haben, probieren Sie es aus und sehen Sie !!!. Das ist der wahre Geist des Lernens.
Ich habe es so gearbeitet:
DROP TRIGGER IF EXISTS mytext_bi;
DELIMITER $$
CREATE TRIGGER mytext_bi BEFORE INSERT ON mytext FOR EACH ROW
BEGIN
DECLARE found_count INT;
SELECT COUNT(1) INTO found_count
FROM mytext WHERE txtmd5 = MD5(LEFT(new.txt,10));
IF found_count = 0 THEN
SELECT COUNT(1) INTO found_count FROM table_that_does_not_exist;
END IF;
SET new.txtmd5 = MD5(LEFT(new.txt,10));
END $$
DELIMITER ;
Die Art der gespeicherten Verfahren besteht darin, entweder den vollständigen Code zu durchlaufen, der durch IF-Then-Blöcke wie in meiner Antwort oder in meiner Antwort verwendet wird oder die Signalverarbeitung verwendet wird (es ist wiederum nicht vollständig operativ).
Willkommen im DBA Stackexchange !!! Wieder +1 wie versprochen.
Entschuldigung für die lange Bearbeitung, aber ich konnte meinen Beispielcode nicht in einen Kommentar legen.
Zunächst einmal: Danke für '+1' :-)
Und - Sie haben Recht: Die Deaktivierung eines Auslösers verhindert die Handlung selbst nicht. Es gibt keine "korrekte" Möglichkeit, diese Art von "abortieren" -Signal zu tun, die momentan von MySQL implementiert wird.
Die gemeinsame "Problemumgehung" ist - wie Sie in Ihrer Antwort beschrieben haben, um eine SQL -Ausnahme zu erhöhen. Die gesamte Aktion wird also durch einen Nebeneffekt (die Ausnahme) verhindert.
Aber: Sei vorsichtig. Diese "Lösung" erzeugt manchmal mehr Probleme - denken Sie über transaktionsübergreifende Trigger und hoffentlich kontrolliert - Rollbacks. UUUUPS! (Das ist, warum ich die Kontingennzeichen für diesen Ansatz verwendet habe).
Darüber hinaus ist es ein wirklich schweres Zeug, solche Implementierungen zu debuggen. Mit diesem Verhalten muss der Entwickler, der Administrator oder den Betreuer in der Lage sein, zwischen "guten" SQL-Ausnahmen zu unterscheiden und wirklich Probleme aufgetreten zu sein. Entsetzlich.
Und zuletzt nicht zuletzt: Diese Art von Einschränkung trifft jede gespeicherte Routinen in MySQL (Verfahren, Funktionen und Auslöser)!
Tatsächlich: Dieser Mangel an Kontrolle ist für jeden Anbieter eines RDBMs eine Schuld. Und yepp - das ist was ich mit "haben Spaß haben" :-)
Richtig, zurück zu den Wurzeln - die Frage war: "einen Auslöser deaktivieren" (nicht die Aktion verhindern). Insbesondere für diese besondere Anforderung ist der Ansatz sicher und zuverlässig.
Ein weiterer Informationen "Hinter dem Szene": Der bereitgestellte Ansatz funktioniert sicher, da MySQL mit NULL mit NULL mit "@Triigger_disabled" initialisiert Sitzungsvariablen initialisieren - dies ist auch ein Nebeneffekt.
Und bitte denken Sie daran: Diese Art von Variablen sind nur für die aktuelle Sitzung sichtbar! Sie können also den Auslöser für Ihre aktuelle Sitzung deaktivieren. Diese Art von Implementierungen hat keine "serverweiten" Effekte!
Wenn Sie diese Art von Dingen benötigen (Auslöser für den gesamten Server deaktivieren), müssen Sie serverweite sichtbare und akzessbare Objekte wie Servervariablen (UUUPs - eine andere Art von "Problemumgehung" mit unvorhersehbaren Nebenwirkungen) oder - der saubereren Weg - Tabellen verwenden.
Also, auch "Entschuldigung" für meine lange Antwort und noch einmal: Viel Spaß :-)