MySQL里的换语替换语句,说白了就是换语REPLACE INTO。它跟UPDATE长得像,换语但骨子里是换语两码事——REPLACE是先删后插,UPDATE是换语原地改值。这个区别,换语坑过不少刚入门的换语开发。
举个例子。换语你有一张用户表,换语id是换语主键,存着用户邮箱和昵称。换语某天用户改了昵称,换语你写了一条REPLACE INTO user (id,换语 email, nickname) VALUES (1, 'old@mail.com', '新昵称')。执行完一看,换语数据确实变了,换语但那条记录的create_time字段被重置成了当前时间——因为整行都被删了重新插。这就是先删后插的副作用。你要是没注意,用户注册时间全给你抹了。
那什么时候用REPLACE合适?场景其实挺窄。比如你有个日志表,主键是日期加设备ID,每天每台设备只记一条汇总数据。当天有新的统计结果要覆盖旧值,用REPLACE就挺顺手——反正旧数据没保留价值,直接删了重来,省得写UPDATE加INSERT的条件判断。
但大多数业务场景,UPDATE才是正解。UPDATE只改你指定的列,其他字段纹丝不动。REPLACE这种一刀切的性格,碰上需要保留部分历史字段的表就抓瞎。更麻烦的是,REPLACE还会触发外键的连锁删除——如果别的表引用了这行的主键,外键约束是ON DELETE CASCADE的话,关联数据全给你清空。这可不是闹着玩的。
还有个隐藏陷阱:REPLACE对唯一索引也敏感。只要表里任何一个唯一键跟新数据冲突,它就会删掉旧行。你本意是更新A列,结果B列的唯一索引跟别人撞了,整行被删,数据丢得莫名其妙。我见过线上事故,就是有人拿REPLACE批量同步数据,撞了唯一索引,把不该删的行给删了,最后靠备份恢复。
真要替换,还有个变体叫INSERT ... ON DUPLICATE KEY UPDATE。这货比REPLACE温和——冲突时只更新指定列,不删行。比如你要记录用户最近登录时间,主键是用户ID,写成INSERT INTO login_log (user_id, last_login) VALUES (123, NOW()) ON DUPLICATE KEY UPDATE last_login = NOW()。第一次插入,以后每次只更新时间,其他列安然无恙。这才是大多数场景该用的。
说句实话,REPLACE INTO在MySQL里属于那种“能用但别乱用”的语句。它适合什么?适合你确定整行数据都是可再生、无历史包袱的。比如临时表、缓存表、纯统计表。但凡表里有业务意义的字段,或者被外键引用,老老实实用UPDATE或者ON DUPLICATE KEY UPDATE。
最后提醒一句,REPLACE的执行计划跟INSERT很像,但它内部要先走一遍主键或唯一索引的查找。数据量大时,性能不比UPDATE快。别指望拿它当优化手段。写完SQL前,先问自己一句:这行数据删了重来,代价我扛得住吗?扛不住,就换语句。