12
votes

I have a table that I keep track of fees for a specific item. These fees can change over time so I have two columns (startDate, endDate) with the current set of fees always having an endDate in the far future. I already have a trigger that I use to do some calculations on the new row being entered but what I also want to have happen is if I enter an item that already has an entry I want to set the previous entry's endDate to the day before the new entry's startDate and the new endDate to a predetermined far-away date. Here is the code for what I tried first:

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$$

The error I get is:

ERROR 1442: Can't update table 'split' in stored function/trigger because it is already used by statement which invoked this stored function/trigger.
4
Try doing your update in an AFTER INSERT trigger.Brian Driscoll
@BrianDriscoll I tried that :-( I got the same error.donbyte
The thing is that you can't explicitly update the target of your trigger from within the trigger. You might have to find another way to go about your task.Brian Driscoll
Please show the following : 1) SHOW CREATE TABLE im.split\G and 2) a sample INSERT command into im.split.RolandoMySQLDBA
@RolandoMySQLDBA I am not sure how this helps . . . according to the answers below, it is not possible to update another row in the same table with a trigger. Do you think it is possible?donbyte

4 Answers

14
votes

The answer to this might be unwelcome, but it is: You can't do that.

A Trigger can't update another row of the same table as the row, the trigger was called from.

The typical way to do that, is to create a stored procedure, that inserts into/Updates the target table, then updates the other row(s), all in a transaction.

8
votes

If you have a UNIQUE KEY defined on (procKey,EndDate), then perhaps you can remove the second line of the trigger. Also remove the hardcoded date from the trigger.

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$$

and do an INSERT ON DUPLICATE KEY UPDATE like this:

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

You may also want to define endDate in im.split as follows

enddate DATE DEFAULT '2050-12-31'
3
votes

I managed to get it to work, by creating a "wrapper table" which is defined with the same fields as your target table, but by using the FEDERATED storage engine. The federated server I defined to target the same mysql server/itself, so at "localhost". The trigger I then made change a row in the wrapper table. Of course you have to be very sure what you do in the trigger, to avoid a recursive loop. Also potentially the performance isn't that good; I haven't tested performance, but it has been working for years in a production environment.

1
votes

From the MySQL Docs:

Within a stored function or trigger, it is not permitted to modify a table that is already being used (for reading or writing) by the statement that invoked the function or trigger.

You will need to find some other way of doing what you're trying to do.