ON DUPLICATE KEY UPDATE在有UPDATE触发器时不起作用

时间:2010-11-05 14:21:21

标签: mysql triggers lastinsertid

我有一个INSERT语句,如下所示:

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)

这种做法一直很好。当我添加以下触发器时它停止工作:

CREATE TRIGGER officer_update BEFORE UPDATE ON `officer`
FOR EACH ROW SET NEW.updated_at = NOW(), NEW.created_at = OLD.created_at

并非officer记录未插入。似乎触发器正在劫持LAST_INSERT_ID()或其他东西。我这样说是因为下一个执行的查询是:

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')

由于我使用相同的文件运行了数十个成功的导入,因此我没有更改我的代码,现在我添加此触发器后导入无效,我必须推断出触发器是罪魁祸首。我与另一个表有类似的情况,删除触发器可以解决问题。

所以我的问题是:

  1. 有人可以解释一下, 特别是,导致我的军官 id设置为0?
  2. 什么是好事 解决这个问题?
  3. 我在officer.created_at(还有很多其他表'created_at)上有另一个触发器,我宁愿避免某些尴尬的解决方案,我在created_at上触发了但是DEFAULT CURRENT_TIMESTAMP上的updated_at。出于某种原因,MySQL每个表只允许一个自动时间戳,因此我CURRENT_TIMESTAMPcreated_at都无法updated_at

    以下是SHOW CREATE TABLE的{​​{1}}:

    officer

2 个答案:

答案 0 :(得分:4)

如果INSERT ... ON DUPLICATE KEY UPDATE已存在,您的officer_number似乎是一种防止错误的方法。您是否需要更新(触发TRIGGER),或者您是否可以使用INSERT IGNORE?:

INSERT IGNORE INTO officer (officer_number,
                     name,
                     bank_id)    
VALUES ('',
        '',
        8);

如果officer_id已经存在,那就什么都不做,因此完全不需要更新(因此LAST_INSERT_ID())。

如果那是不可能的,那么也许你的INSERT ... ON DUPLICATE KEY UPDATE可能会被调整。我不清楚目的:

id = LAST_INSERT_ID(id)

LAST_INSERT_ID()(不带任何参数),返回由最近执行的INSERT语句为AUTO_INCREMENT列设置的第一个自动生成的值,以影响此类列。

但是,如果提供参数,则返回该参数的值,并且对LAST_INSERT_ID()的下一次调用(不带任何参数)将返回相同的值。例如:

SELECT LAST_INSERT_ID(100);
+---------------------+
| LAST_INSERT_ID(100) |
+---------------------+
|                 100 |
+---------------------+

SELECT LAST_INSERT_ID();
+------------------+
| LAST_INSERT_ID() |
+------------------+
|              100 |
+------------------+

所以,如果我们假设id == 100,那么这应该是真的:

SELECT LAST_INSERT_ID(id);
+--------------------+
| LAST_INSERT_ID(id) |
+--------------------+
|                100 |
+--------------------+

SELECT LAST_INSERT_ID();
+------------------+
| LAST_INSERT_ID() |
+------------------+
|              100 |
+------------------+

继续之后:

id = LAST_INSERT_ID(id)

应该与:

相同
id = id

或者,正如约什戴维斯所建议的那样,它根本就没有必要。您是否尝试过id = id?排除它后会发生什么?

manual表示:

  

但是,如果你混合引用   LAST_INSERT_ID()和   LAST_INSERT_ID(expr),效果是   未定义

  

生成的ID是   维护在服务器上   每个连接基础。这意味着   函数返回的值   给定的客户是第一个   生成的AUTO_INCREMENT值   最近的声明影响了   该客户的AUTO_INCREMENT列。   此值不受其他人的影响   客户,即使他们产生   AUTO_INCREMENT自己的值。

当您同时使用LAST_INSERT_ID()LAST_INSERT_ID(expr)时,行为未定义。此外,TRIGGER可以被视为一个连接(它直接在服务器上运行),而INSERT和CREATE语句可能从不同的连接调用。鉴于此,以及在版本之间报告与LAST_INSERT_ID相关的各种更改和错误, 可能会导致您的方法出现问题。

回到Josh Davis所说的话,我倾向于在你的INSERT语句中解决id = LAST_INSERT_ID(id)的使用问题。知道如何在officer_id语句中派生INSERT INTO account - 即收到零值的语句也是有帮助的。

答案 1 :(得分:0)

我不明白您为何尝试更新id。你不能只是删除对LAST_INSERT_ID()的电话吗?

INSERT INTO officer (officer_number,
                     name,
                     bank_id)
VALUES ('',
        '',
        8)

ON DUPLICATE KEY UPDATE officer_number = '',
                        name = '',
                        bank_id = 8

此外,您应该发布您的MySQL版本(以及使用的引擎),因为过去LAST_INSERT_ID()行为有一些变化。