2017-02-21 117 views
4

我與MySQL 5.7的工作:MySQL之前觸發INSERT重複密鑰更新 - 手動似乎是錯誤的?

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未被重新生成。我用恆定的字符串嘗試了這一點,並得到相同的結果。

現在,這正是我想要發生的事情;一個BEFORE INSERT不管upsert的哪個分支被採用,都是無用的,或者至少這種行爲似乎更有用。但似乎與手冊所說的相反。任何見解?

+0

我的猜測:會生成新的'uuid'但不分配。你爲什麼認爲它應該?你可以確切地知道在複製鍵上應該發生什麼:'name ='Alfred''。那麼你爲什麼期望UUID改變? –

+0

@PaulSpiegel謝謝你的想法,但我有「設置」那裏。它在初始插入過程中起作用 - 這就是列首先被填充的方式。 – fool4jesus

回答

3

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。您的代碼中沒有任何內容會告訴您這麼做。

如果你想在新的UUID(在觸發產生)在對重複部分進行分配,你可以用values(uuid)存取權限它:

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

我明白了。你所說的是觸發器正在運行,並且正在填充「新」記錄,但它實際上並沒有在更新情況下做任何事情,除非你使用values()函數在UPDATE子句中引入它。這就說得通了。謝謝! – fool4jesus