运营提了个需求:把商品描述里的「包邮」统一改成「全场包邮」。几万行数据,用 UPDATE 一条条改不现实,这时候该想到 mysql replace。它其实包含两个完全不同的东西,用错一次就够记一辈子。
REPLACE() 只改返回值,不动原表
语法是 REPLACE(原字符串, 要查找的内容, 替换成的内容),区分大小写,找不到就原样返回。先看一眼效果:
SELECT REPLACE('京东包邮正品','包邮','全场包邮'); -- 结果:京东全场包邮正品要落到表里,得配合 UPDATE 使用:
UPDATE goods SET intro = REPLACE(intro,'包邮','全场包邮') WHERE intro LIKE '%包邮%';WHERE 别省,否则全表每一行都会被重写,binlog 和锁的开销都上来了,大表上尤其明显。另外别在 WHERE 里套 REPLACE,比如 WHERE REPLACE(title,'a','b')='x',这样用不上索引,几百万行会扫得很慢。
REPLACE INTO 是另一回事
REPLACE INTO 不是字符串函数,是整行写入语句。主键或唯一索引冲突时,它先删掉旧行再插入新行,没有冲突就普通插入。写法有两种:
REPLACE INTO goods(id,title) VALUES(1,'新款');副作用很实在:自增 ID 会变,触发器按删除加插入触发两次,外键有级联删除时连带数据一起没。多数情况用 INSERT ... ON DUPLICATE KEY UPDATE 更稳,只更新指定字段,不会把整行推倒重来。
改之前备份,改完核对行数
执行 UPDATE 前把 WHERE 条件换成 SELECT COUNT(*),确认影响范围。顺手导一份表出来:
mysqldump -u root -p 库名 goods > goods_bak.sql改完再查一次同样的条件,结果应该是 0。线上表建议先在测试库跑一遍,mysql replace 这类批量操作一旦条件写错,回滚比想象中麻烦。