【问题标题】:MySQL before trigger for INSERT ON DUPLICATE KEY UPDATE - manual seems wrong?MySQL 在触发 INSERT ON DUPLICATE KEY UPDATE 之前 - 手动似乎错误?
【发布时间】:2017-02-21 22:00:36
【问题描述】:

我正在使用 MySQL 5.7:

D:\>mysql --version
mysql  Ver 14.14 Distrib 5.7.17, for Win64 (x86_64)

根据manual,BEFORE INSERT 触发器的行为应该是:

为每一行激活一个 BEFORE INSERT 触发器,后跟一个 AFTER INSERT 触发器或 BEFORE UPDATE 和 AFTER UPDATE 同时触发 触发器,取决于该行是否存在重复键。

我认为这意味着无论是否存在重复的键匹配,都会执行 BEFORE INSERT,而 AFTER INSERT 和 UPDATE 触发器取决于是否存在键冲突。 This SO 重复相同。但是,我没有看到这种行为。这是我所做的:

create table testtable (
  id integer primary key auto_increment,
  nickname varchar(40),    -- this is the natural key to be unique indexed
  name varchar(40),
  uuid varchar(36));       -- this is what I want to assign in the trigger

alter table testtable add unique index testtable_ux (nickname);
create trigger testtable_uid before insert on testtable for each row set
  new.uuid=uuid();

-- get some data
insert into testtable (nickname, name) values ('bob', 'Robert'), 
  ('fred', 'Frederick'), ('cha', 'Chauncey');
select * from testtable;

1   bob   Robert      06fb18be-f87e-11e6-8e6f-0060737a7c01
2   fred  Frederick   06fb1a5d-f87e-11e6-8e6f-0060737a7c01
3   cha   Chauncey    06fb1aec-f87e-11e6-8e6f-0060737a7c01

很好 - 每个家伙都有一个唯一的 UUID。现在好的,根据手册,无论是否存在重复键,都应该执行此 BEFORE INSERT 触发器,所以即使我得到重复键更新,UUID 也应该更新 - 对吧?让我们看看:

insert into testtable (nickname, name) values ('fred', 'Alfred') 
  on duplicate key update name='Alfred';
3   88  16:39:32 ... 2 row(s) affected  0.032 sec

select * from testtable;
1   bob   Robert      06fb18be-f87e-11e6-8e6f-0060737a7c01
2   fred  Alfred      06fb1a5d-f87e-11e6-8e6f-0060737a7c01
3   cha   Chauncey    06fb1aec-f87e-11e6-8e6f-0060737a7c01

嗯,名称已更新。让我们比较一下 UUID:

2   fred  Frederick   06fb1a5d-f87e-11e6-8e6f-0060737a7c01
2   fred  Alfred      06fb1a5d-f87e-11e6-8e6f-0060737a7c01

upsert 已按预期执行,但未重新生成 UUID。我已经尝试使用常量字符串并得到相同的结果。

现在,这正是我想要发生的;无论采用 upsert 的哪个分支,都发生 BEFORE INSERT 似乎毫无用处,或者至少这种行为似乎更有用。但这似乎与手册所说的相反。有什么见解吗?

【问题讨论】:

  • 我的猜测:新的uuid 已生成但未分配。为什么你认为应该?你确切地告诉重复密钥应该发生什么:name='Alfred'。那么,您为什么希望 UUID 发生变化?
  • @PaulSpiegel 感谢您的想法,但我在那里有“设置”。它在初始插入期间起作用 - 这就是首先填充列的方式。

标签: mysql upsert


【解决方案1】:

BEFORE INSERT 触发器在重复时触发。您可以使用日志表对其进行测试。我已在您的代码中添加了这样一个表并修改了触发器以填充该表:

drop table if exists testtable;
create table testtable (
  id integer primary key auto_increment,
  nickname varchar(40),    -- this is the natural key to be unique indexed
  name varchar(40),
  uuid varchar(36));       -- this is what I want to assign in the trigger

alter table testtable add unique index testtable_ux (nickname);

drop table if exists testlog;
create table testlog (
  log_id int primary key auto_increment,
  nickname varchar(40),
  name varchar(40),
  uuid varchar(36)
);

drop trigger if exists testtable_uid;
delimiter //
create trigger testtable_uid before insert on testtable for each row
begin
  set new.uuid=uuid();
  insert into testlog (nickname, name, uuid) values (new.nickname, new.name, new.uuid);
end //
delimiter ;

insert into testtable (nickname, name) values ('bob', 'Robert'), 
  ('fred', 'Frederick'), ('cha', 'Chauncey');
select * from testtable;

insert into testtable (nickname, name) values ('fred', 'Alfred') 
  on duplicate key update name='Alfred';

select * from testtable;
select * from testlog;

您会看到testlog 表有4 行。最后一个包含“fred”、“Alfred”和一个新的 UUID。这意味着触发器已被触发。这也意味着已经生成了一个新的 UUID。但是那个 UUID 没有分配给testtable.uuid。您的代码中没有任何内容可以告诉您这样做。

如果您希望在 ON DUPLICATE 部分中分配新的 UUID(在触发器中生成),您可以使用 values(uuid) 访问它:

insert into testtable (nickname, name) values ('fred', 'Alfred') 
  on duplicate key update 
    name='Alfred',
    `uuid`=values(`uuid`);

【讨论】:

  • 我明白了。您所说的是触发器正在运行并且正在填充“新”记录,但它实际上在更新案例中并没有做任何事情,除非您在带有 values() 函数的 UPDATE 子句中将其引入。那讲得通。谢谢!
猜你喜欢
  • 2011-08-09
  • 1970-01-01
  • 2023-02-06
  • 2010-10-07
  • 1970-01-01
  • 2018-06-27
  • 2017-04-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多