web-dev-qa-db-ja.com

MySQLトリガーが新しい行の値を設定し、同じテーブル内の別の行を更新します

特定のアイテムの料金を追跡するテーブルがあります。これらの料金は時間の経過とともに変化する可能性があるため、2つの列(startDate、endDate)があり、現在の料金セットには常に遠い将来のendDateがあります。入力された新しい行に対して計算を行うために使用するトリガーがすでにありますが、既にエントリがあるアイテムを入力した場合は、前のエントリのendDateを前日に設定します。新しいエントリのstartDateと新しいendDateを事前に定義された遠い日付にします。これが私が最初に試したもののコードです:

CREATE
DEFINER=`root`@`%`
TRIGGER `im`.`splitBeforeIns`
BEFORE INSERT ON `im`.`split`
FOR EACH ROW
BEGIN
    SET NEW.tcPercent = (NEW.tcOfficeFee / NEW.globalFee) * 100 , NEW.proPercent = 100 - NEW.tcPercent, NEW.endDate = 20501231;
    UPDATE im.split set endDate = ADDDATE(NEW.startDate, -1) where procKey = NEW.procKey AND endDate = 20501231;
END$$

私が得るエラーは次のとおりです。

ERROR 1442: Can't update table 'split' in stored function/trigger because it is already used by statement which invoked this stored function/trigger.
11
donbyte

これに対する答えは歓迎されないかもしれませんが、それは次のとおりです。それはできません。

トリガーはできません同じテーブルの別の行を更新します行として、トリガーが呼び出されました。

これを行う一般的な方法は、すべてトランザクション内で、ターゲットテーブルに挿入/更新してから、他の行を更新するストアドプロシージャを作成することです。

13
Eugen Rieck

(procKey、EndDate)にUNIQUE KEYが定義されている場合は、トリガーの2行目を削除できます。また、ハードコードされた日付をトリガーから削除します。

CREATE
DEFINER=`root`@`%`
TRIGGER `im`.`splitBeforeIns`
BEFORE INSERT ON `im`.`split`
FOR EACH ROW
BEGIN
    SET NEW.tcPercent = (NEW.tcOfficeFee / NEW.globalFee) * 100 , NEW.proPercent = 100 - NEW.tcPercent;
END$$

INSERT ON DUPLICATE KEY UPDATE 次のようにします。

INSERT INTO im.split ...
ON DUPLICATE KEY UPDATE
endDate = ADDDATE(startDate, -1);

次のようにim.splitでendDateを定義することもできます。

enddate DATE DEFAULT '2050-12-31'
8
RolandoMySQLDBA

ターゲットテーブルと同じフィールドで定義される「ラッパーテーブル」を作成することで、それを機能させることができましたが、FEDERATEDストレージエンジンを使用しました。同じmysqlサーバー/それ自体をターゲットにするように定義したフェデレーションサーバー、つまり「localhost」。次に、私が行ったトリガーによって、ラッパーテーブルの行が変更されました。もちろん、再帰的なループを回避するために、トリガーで何をするかを非常に確実にする必要があります。また、パフォーマンスがそれほど良くない可能性もあります。パフォーマンスはテストしていませんが、実稼働環境で何年も機能しています。

3
user3008781

MySQL Docs から:

ストアド関数またはトリガー内で、関数またはトリガーを呼び出したステートメントによって(読み取りまたは書き込みのために)すでに使用されているテーブルを変更することは許可されていません。

あなたがやろうとしていることをする他の方法を見つける必要があるでしょう。

1
Brian Driscoll