解决MySQL复合主键下ON DUPLICATE KEY UPDATE语句失效问题

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介:

先描述一下这个问题的起因,假设有一张表,里面保存了交易订单,每张订单有唯一的ID,有最后更新时间,还有数据,详情如下:

1
2
3
4
5
6
7
+ -------+----------+------+-----+---------------------+-------+
| Field | Type     |  Null  Key  Default              | Extra |
+ -------+----------+------+-----+---------------------+-------+
| UID   |  int (11)  |  NO    | PRI | 0                   |       |
Time   | datetime |  NO    |     | 0000-00-00 00:00:00 |       |
| Data  |  int (11)  | YES  |     |  NULL                 |       |
+ -------+----------+------+-----+---------------------+-------+


针对这张表会做追加及更新的操作,具体来说就是如果订单不存在就INSERT一条新的,如果已存在就UPDATE。由于入库前无法得知相应记录是否已存在,通常的做法无法以下几种:

1、先SELECT一下,再决定INSERT还是UPDATE;

2、直接UPDATE,如果受影响行数是0,再INSERT;

3、直接INSERT,如果发生主键冲突,再UPDATE;

这几种方法都有缺陷,对MySQL来说其实最好的是直接利用INSERT...ON DUPLICATE KEY UPDATE...语句,具体到上面的test表,执行语句如下 :

1
INSERT  INTO  test  VALUES  (1,  '2016-1-1' , 10)  ON  DUPLICATE  KEY  UPDATE  Time = '2016-1-1' ,Data=10;

可以很好的插入或更新数据,一条语句就搞定,至此一直工作得很好。


后来因为查询方式变更,要求将UID和Time两个字段做联合主键,此时表结构如下:

1
2
3
4
5
6
7
+ -------+----------+------+-----+---------------------+-------+
| Field | Type     |  Null  Key  Default              | Extra |
+ -------+----------+------+-----+---------------------+-------+
| UID   |  int (11)  |  NO    | PRI | 0                   |       |
Time   | datetime |  NO    | PRI | 0000-00-00 00:00:00 |       |
| Data  |  int (11)  | YES  |     |  NULL                 |       |
+ -------+----------+------+-----+---------------------+-------+


但是问题来了:一但Time字段被更新,即使是相同的UID,也被数据库认为是不同的主键,因此不会产生主键冲突,上面的语句就失效了,数据库里出现了很多UID相同的数据。


开始寻找解决办法,其实也简单,按MySQL文档里的说明,ON DUPLICATE KEY UPDATE语句判断是否冲突是依靠主键或唯一索引,因此为UID建立唯一索引就可以了。先建索引:

1
CREATE  UNIQUE  INDEX  IDX_UID  ON  test(UID);



再测试一下插入:

1
2
INSERT  INTO  test  VALUES  (1,  '2016-1-1' , 10)  ON  DUPLICATE  KEY  UPDATE  Time = '2016-1-1' ,Data=10;
INSERT  INTO  test  VALUES  (1,  '2016-2-1' , 20)  ON  DUPLICATE  KEY  UPDATE  Time = '2016-2-1' ,Data=20;



检查数据库,可以看到不会有多条数据生成,唯一的一条数据是Data字段被更新成20的,成功。




     本文转自 BoyTNT 51CTO博客,原文链接:http://blog.51cto.com/boytnt/1736690,如需转载请自行联系原作者

相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
24天前
|
SQL 关系型数据库 MySQL
【MySQL】11. 复合查询(重点)
【MySQL】11. 复合查询(重点)
20 0
|
3月前
|
NoSQL 关系型数据库 MySQL
MySQL主键与索引
MySQL主键与索引
58 1
|
2月前
|
SQL 关系型数据库 MySQL
MySQL技能完整学习列表3、SQL语言基础——1、SQL(Structured Query Language)简介——2、基本SQL语句:SELECT、INSERT、UPDATE、DELETE
MySQL技能完整学习列表3、SQL语言基础——1、SQL(Structured Query Language)简介——2、基本SQL语句:SELECT、INSERT、UPDATE、DELETE
51 0
|
3月前
|
SQL 存储 缓存
SQL语句在MySQL中是如何执行的
SQL语句在MySQL中是如何执行的
46 0
|
24天前
|
关系型数据库 MySQL
【MySQL】10. 复合查询(重点)
【MySQL】10. 复合查询(重点)
13 0
|
27天前
|
关系型数据库 MySQL
MySQL创建表出现 Specified key was too long; max key length is 767 bytes
MySQL创建表出现 Specified key was too long; max key length is 767 bytes
20 2
|
29天前
|
缓存 关系型数据库 MySQL
为啥MySQL官方不推荐使用uuid或者雪花id作为主键
为啥MySQL官方不推荐使用uuid或者雪花id作为主键
23 1
|
SQL Oracle 关系型数据库
【mysql】—— 复合查询
【mysql】—— 复合查询
|
2月前
|
存储 关系型数据库 MySQL
用雪花 ID 和 UUID 做 MySQL 主键,可以吗?
用雪花 ID 和 UUID 做 MySQL 主键,可以吗?
32 0
用雪花 ID 和 UUID 做 MySQL 主键,可以吗?
|
3月前
|
存储 缓存 关系型数据库
MySQL update执行流程到 redo log深入理解
MySQL update执行流程到 redo log深入理解
49 0