質問
トリガーを一時的に無効にすることは可能ですが、テーブルは1つだけです。
たとえば、テーブル、タブレアが挿入、更新、削除トリガーがあります。同じトリガーを備えたテーブルBもありますが、表Aの特定の列のみに影響します。
現在、両方のテーブルを使用する更新クエリがあります。表Aの更新はトリガーを発射する必要があることを知っていますが、表Bの更新は間違いなくトリガーを発射する必要はありません。そのため、更新が完了するまで、これらのトリガーを無効にしたいと思います。
これは可能ですか? MySQL 5.1を使用しています
補遺
これが本質的にトリガーテーブルです
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
以下は、本質的にテーブルBのトリガーです
CREATE TRIGGER `usergroups_comments_insert` AFTER INSERT ON `usergroups_comment`
FOR EACH ROW
BEGIN
CALL sp-set-comment_count(NEW.`gid`);
END;
これが表Bから発射されたストアドプロシージャです
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 $$
解決
実際、すべてのトリガーにIFをブロックする場合、すべてのトリガーを効果的にシャットダウンすることができます。これがそのようなコードブロックです
IF @TRIGGER_DISABLED = 0 THEN
...trigger body
END IF;
MySQL環境では、できます
- 走る
SET @TRIGGER_DISABLED = 1;
- データメンテナンスを行います
- 走る
SET @TRIGGER_DISABLED = 0;
したがって、表Aのトリガーは次のようになります。
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
したがって、テーブルBのトリガーは次のようになります。
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;
テーブルAのトリガーを起動するがテーブルBにしない場合は、テーブルBのトリガーにコードブロックのみを追加します。
他のヒント
このアプローチを簡素化できます。
- 最初の「開始」ブロックにラベルを追加するだけです。
- テスト、制御変数がnullでないかどうか。
- はいの場合、トリガーを残します。
したがって、元のトリガーコードを「if」ステートメントにラップすることを避けることができます。トリガーヘッドのみを明確に定義された方法で変更する必要があります - これはよりシンプルで、はるかに信頼性があります。
例:
CREATE TRIGGER [YOUR_TRIGGER_SPEC]
Trigger: BEGIN
IF @TRIGGER_DISABLED NOT NULL THEN
LEAVE Trigger;
END IF;
[YOUR CODE]
END;
楽しむ :-)
Rolandomysqldbaによる編集2012-05-01 18:38 EDT
私は実際にその数ヶ月前に試しましたが、それは不安定です。方法は次のとおりです。
サンプルテーブル
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>
これがトリガーです:
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 ;
私が「ドミニク・エドワーズ」を挿入しようとするところで何が起こるか見てください
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>
ああ、とにかく滑った!!!
私はまだあなたの努力のためだけでなく、まったく同じことを試したからです(近くにラベルを置いたからです BEGIN
)そしてそれは私にとってはうまくいきませんでした。なんて学ぶ方法でしょう。
MySQLストアドプロシージャ言語(信号処理は適切に実装されていない)に完全な信頼を置くべきではなく、「楽しんでください」と言ったときに強調しました。 時々、楽しむことはそれを試してみて見ることを意味します!!!. 。それが学習の真の精神です。
私はこのように取り組んだ:
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 ;
ストアドプロシージャの性質は、回答で行ったように、If-Thenブロックをナビゲートする完全なコードを通過するか、信号処理を使用することです(繰り返しますが、完全に動作しません)
DBA stackexchangeへようこそ!!!繰り返しますが、約束されているように+1。
長い編集で申し訳ありませんが、コメントに例を掲載することはできませんでした。
まず第一に:「+1」をありがとう:-)
そして - あなたは正しいです:トリガーの無効化は、自分でアクションを妨げません。現時点でMySQLによって実装されたこの種の「中止」シグナルを行う「正しい」方法はありません。
一般的な「回避策」は、あなたがあなたの答えで説明したように、SQL -Exceptionを提起することです。したがって、アクション全体が副作用(例外)によって防止されます。
しかし、注意してください。この「ソリューション」は、トランザクショントリガーと再Quired(そしてできれば制御されたロールバック)について考えてみることもあります。 uuuups! (それは、なぜこのアプローチに割り当てマークを使用したのか)。
さらに、このような種類の実装をデバッグするのは非常に難しいことです。この動作により、開発者、管理者、またはメンテナーは、「良い」SQL-exceptionsを区別し、実際に問題が発生した必要があります。最悪。
そして最後に、この種の制限は、MySQLに保存された各ルーチン(手順、関数、トリガー)にヒットします!
実際、このコントロールの欠如は、RDBMSの各プロバイダーにとって責任です。そしてYepp-これは私が「楽しんでください」という意味です:-)
右、ルーツに戻る - 質問は、「トリガーを無効にする」ことでした(アクションを妨げないでください)。特に、この特別な要件については、アプローチは安全で信頼性があります。
「Szeneの背後に」というもう1つの情報:提供されたアプローチは安全に機能します。これは、nullを使用して「@trigger_disabled」のようなセッションベースの変数を初期化するため、これも副作用です。
留意してください:この種の変数は、現在のセッションにのみ表示されます!したがって、現在のセッションのトリガーを無効にすることができます。この種の実装には、「サーバー全体」の効果はありません!
この種のもの(サーバー全体のトリガーを無効にする)が必要な場合は、サーバー変数(UUUPS-予測不可能な副作用を備えた別の種類の「回避策」などのサーバー全体の可視オブジェクトを使用する必要があります。
だから、私の長い反応のために「ごめんなさい」ともう一度:楽しんでください:-)