I have an INSERT statement that looks like this:
INSERT INTO officer (officer_number,
name,
bank_id)
VALUES ('',
'',
8)
ON DUPLICATE KEY UPDATE officer_number = '',
name = '',
bank_id = 8,
id = LAST_INSERT_ID(id)
This way of doing it has been working just fine. It stopped working when I added the following trigger:
CREATE TRIGGER officer_update BEFORE UPDATE ON `officer`
FOR EACH ROW SET NEW.updated_at = NOW(), NEW.created_at = OLD.created_at
It’s not that the officer record isn’t getting inserted. It just seems that the trigger is hijacking LAST_INSERT_ID() or something. I say this because the next query that’s executed is this:
INSERT INTO account (import_id,
branch_id,
account_number,
officer_id,
customer_id,
open_date,
maturity_date,
interest_rate,
balance,
opening_balance)
VALUES ('123',
'4567',
'789',
'0', # This is the officer id which is of course invalid
'321',
'1992-04-22',
'2012-05-22',
'0.0123',
'0',
'10000')
Since I’ve run dozens of successful imports with the same exact file, I haven’t changed my code, and now my imports aren’t working after I added this trigger, I must deduce that the trigger is the culprit. I had a similar situation with another table and removing the trigger fix the problem.
So my questions are:
- Can someone explain what,
specifically, is causing my officer
id to get set to 0? - What’s a good
solution to this problem?
I have another trigger on officer.created_at (and a lot of other tables’ created_ats) and I would prefer to avoid some sort of awkward solution where I have a trigger on created_at but a DEFAULT CURRENT_TIMESTAMP on updated_at. For some reason, MySQL only allows one auto-timestamp per table, so I can’t do CURRENT_TIMESTAMP for both created_at and updated_at.
Here is the SHOW CREATE TABLE for officer:
CREATE TABLE `officer` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`officer_number` varchar(255) NOT NULL,
`name` varchar(255) NOT NULL,
`bank_id` bigint(20) NOT NULL,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id`),
UNIQUE KEY `officer_number` (`officer_number`,`name`),
UNIQUE KEY `officer_number_2` (`officer_number`,`bank_id`),
KEY `bank_id` (`bank_id`),
CONSTRAINT `officer_ibfk_1` FOREIGN KEY (`bank_id`) REFERENCES `bank` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=102735 DEFAULT CHARSET=latin1
Your
INSERT ... ON DUPLICATE KEY UPDATEappears to be a way of preventing an error ifofficer_numberalready exists. Do you need the update to happen (to fire the TRIGGER), or could you instead useINSERT IGNORE?:That would simply do nothing if
officer_idalready exists, thus removing the need for the update (and thereforeLAST_INSERT_ID()) altogether.If that is not possible, then perhaps your
INSERT ... ON DUPLICATE KEY UPDATEcould be tweaked. I’m not clear on the purpose of:LAST_INSERT_ID()(without any arguments), returns the first automatically generated value that was set for an AUTO_INCREMENT column by the most recently executed INSERT statement to affect such a column.However, if you supply an argument, it returns the value of that argument, and the next call to
LAST_INSERT_ID()(without any arguments), returns the same value. For example:So, if we assume that
id == 100, then this should be true:Following on from that:
Should be the same as:
Or, as suggested by Josh Davis, it shouldn’t be necessary at all. Have you tried simply
id = id? What exactly happens when you exclude it?The manual states that:
and:
As you are using both
LAST_INSERT_ID()andLAST_INSERT_ID(expr), the behaviour is undefined. Furthermore, the TRIGGER may be regarded as being one connection (it’s run directly on the server), whereas the INSERT and CREATE statements are possibly called from a different connection. Given this, and the various changes and bugs that have been reported associated with LAST_INSERT_ID between versions, it’s likely that there will be problems with your approach.Going back to what Josh Davis said, I’d be inclined to resolve the use of
id = LAST_INSERT_ID(id)in your INSERT statement. It would also be helpful to know how you derive theofficer_idin yourINSERT INTO accountstatement – the one which is receiving a zero value.