MySQL必知必会05:正确设置主键
阅读整理自《MySQL 必知必会》- 朱晓峰,详细内容请登录 极客时间 官网购买专栏。
文章目录
- 业务字段做主键
- 使用自增字段做主键
- 手动赋值字段做主键
- 小结
在一个项目中,客户要进行会员营销,相应的,就需要处理会员信息。会员信息表(demo.membermaster)的设计大体如下,为了能够唯一地标识一个会员的信息,需要为会员信息表设置一个主键。怎么为这个表设置主键,才能达到理想的目标?
cardno (卡号) |
membername (名称) |
memberphone (电话) |
memberpid (身份证号) |
address (地址) |
sex (性别) |
birthday (生日) |
---|---|---|---|---|---|---|
10000001 | 张三 | 18758079160 | 110123200001017890 | 北京 | 男 | 2000-01-01 00:00:00 |
10000002 | 李四 | 18758079161 | 123123199001012356 | 上海 | 女 | 1990-01-01 00:00:00 |
三种设置主键的思路:业务字段做主键、自增字段做主键和手动赋值字段做主键。
业务字段做主键
会员卡号(cardno)看起来比较合适,因为会员卡号不能为空,而且有唯一性,可以用来标识一条会员记录。
mysql> create table demo.membermaster-> (-> cardno char(8) primary key,-> membername text,-> memberphone text,-> memberpid text,-> memberaddress text,-> sex text,-> birthday datetime-> );
Query OK, 0 rows affected (0.10 sec)mysql> describe demo.membermaster;
+---------------+----------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------------+----------+------+-----+---------+-------+
| cardno | char(8) | NO | PRI | NULL | |
| membername | text | YES | | NULL | |
| memberphone | text | YES | | NULL | |
| memberpid | text | YES | | NULL | |
| memberaddress | text | YES | | NULL | |
| sex | text | YES | | NULL | |
| birthday | datetime | YES | | NULL | |
+---------------+----------+------+-----+---------+-------+
7 rows in set (0.02 sec)
会员卡号做主键有没有什么问题?插入 2 条数据来验证下:
mysql> insert into demo.membermaster-> (cardno, membername, memberphone, memberpid, memberaddress, sex, birthday)-> values-> ('10000001', '张三', '18758079160', '110123200001017890', '北京', '男', '2000-01-01');
Query OK, 1 row affected (0.01 sec)mysql> insert into demo.membermaster-> (cardno, membername, memberphone, memberpid, memberaddress, sex, birthday)-> values ('10000002','李四','18758079161', '123123199001012356', '上海', '女', '1990-01-01');
Query OK, 1 row affected (0.01 sec)mysql> select * from demo.membermaster;
+----------+------------+-------------+--------------------+---------------+------+---------------------+
| cardno | membername | memberphone | memberpid | memberaddress | sex | birthday |
+----------+------------+-------------+--------------------+---------------+------+---------------------+
| 10000001 | 张三 | 18758079160 | 110123200001017890 | 北京 | 男 | 2000-01-01 00:00:00 |
| 10000002 | 李四 | 18758079161 | 123123199001012356 | 上海 | 女 | 1990-01-01 00:00:00 |
+----------+------------+-------------+--------------------+---------------+------+---------------------+
2 rows in set (0.00 sec)
不同的会员卡号对应不同的会员,字段“cardno”唯一地标识某一个会员。
实际情况是,存在“cardno”无法唯一识别某一个会员的问题。因为会员卡号存在重复使用的情况。比如,张三不再到商家的门店消费了(退还了会员卡),于是张三就不再是这个商家门店的会员了。商家不想让这个会员卡空着,就把卡号是“10000001”的会员卡发给了王五。
从系统设计的角度看,这个变化只是修改了会员信息表中的卡号是“10000001”这个会员信息,并不会影响到数据一致性。也就是说,修改会员卡号是“10000001”的会员信息,系统的各个模块,都会获取到修改后的会员信息,不会出现“有的模块获取到修改之前的会员信息,有的模块获取到修改后的会员信息,而导致系统内部数据不一致”的情况。因此,从信息系统层面上看是没问题的。但是从使用系统的业务层面来看,就有很大的问题了,会对商家造成影响。
比如有一个销售流水表,记录了所有的销售流水明细。2020 年 12 月 01 日,张三在门店购买了一本书,消费了 89 元。那么,系统中就有了张三买书的流水记录,如下所示:
transactionno (流水单号) |
itemnumber (商品编号) |
quantity (销售数量) |
price (价格) |
salesvalue (销售金额) |
cardno (会员卡号) |
transdate 2020-12-01 |
---|---|---|---|---|---|---|
1 | 1 | 1 | 89 | 89 | 10000001 | 2020-12-01 |
创建销售流水表:
mysql> create table demo.trans-> (-> transactionno int, itemnumber int, quantity decimal(10,3), price decimal(10,2), salesvalue decimal(10,2), cardno char(8), transdate datetime-> );
Query OK, 0 rows affected (0.05 sec)
插入一条销售流水:
mysql> insert into demo.trans-> (transactionno, itemnumber, quantity, price, salesvalue, cardno, transdate)-> values-> (1, 1, 1, 89, 89, '10000001', '2020-12-01');
Query OK, 1 row affected (0.01 sec)
查询一下 2020 年 12 月 01 日的会员销售记录:
mysql> select b.membername, c.goodname, a.quantity, a.salesvalue, a.transdate-> from demo.trans as a-> join demo.membermaster as b-> join demo.goodsmaster as c-> on (a.cardno = b.cardno and a.itemnumber = c.itemnumber);
+------------+----------+----------+------------+---------------------+
| membername | goodname | quantity | salesvalue | transdate |
+------------+----------+----------+------------+---------------------+
| 张三 | book | 1.000 | 89.00 | 2020-12-01 00:00:00 |
+------------+----------+----------+------------+---------------------+
假设会员卡 10000001 又发给了王五,需要更改会员信息表:
mysql> update demo.membermaster-> set membername = '王五',-> memberphone = '13698765432',-> memberpid = '475145197001012356',-> memberaddress = '天津',-> sex = '女',-> birthday = '1970-01-01'-> where cardno = '10000001';
Query OK, 1 row affected (0.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0mysql> select * from demo.membermaster;
+----------+------------+-------------+--------------------+---------------+------+---------------------+
| cardno | membername | memberphone | memberpid | memberaddress | sex | birthday |
+----------+------------+-------------+--------------------+---------------+------+---------------------+
| 10000001 | 王五 | 13698765432 | 475145197001012356 | 天津 | 女 | 1970-01-01 00:00:00 |
| 10000002 | 李四 | 18758079161 | 123123199001012356 | 上海 | 女 | 1990-01-01 00:00:00 |
+----------+------------+-------------+--------------------+---------------+------+---------------------+
2 rows in set (0.00 sec)
再次运行之前的会员消费流水查询:
mysql> select b.membername, c.goodname, a.quantity, a.salesvalue, a.transdate-> from demo.trans as a-> join demo.membermaster as b-> join demo.goodsmaster as c-> on (a.cardno = b.cardno and a.itemnumber = c.itemnumber);
+------------+----------+----------+------------+---------------------+
| membername | goodname | quantity | salesvalue | transdate |
+------------+----------+----------+------------+---------------------+
| 王五 | book | 1.000 | 89.00 | 2020-12-01 00:00:00 |
+------------+----------+----------+------------+---------------------+
1 row in set (0.00 sec)
很明显,这个结果把张三的消费行为放到王五身上去了,肯定是不对的。这里的原因就是,我们把会员卡号是“10000001”的会员信息改了,而会员卡号是主键,会员消费查询通过会员卡号关联到会员信息,得到了完全错误的结果。
所以,千万不能把会员卡号当做主键。实际情况下,不管是会员电话号码、身份证号都不适合作为主键。
**尽量不要用业务字段,也就是跟业务有关的字段做主键。**毕竟,作为项目设计的技术人员,谁也无法预测在项目的整个生命周期中,哪个业务字段会因为项目的业务需求而有重复,或者重用之类的情况出现。
使用自增字段做主键
给会员信息表添加一个字段,比如叫 id,给这个字段定义自增约束,这样就有了一个具备唯一性的,而且不为空的字段来做主键了。
修改一下会员信息表的结构,添加一个自增字段做主键。
第一步,修改会员信息表,删除表的主键约束,这样,原来的主键字段,就不再是主键了。
不过需要注意的是,删除主键约束,并不会删除字段:
mysql> alter table demo.membermaster-> drop primary key;
Query OK, 2 rows affected (0.15 sec)
Records: 2 Duplicates: 0 Warnings: 0
第二步,修改会员信息表,添加字段“id”为主键,并且给它定义自增约束:
mysql> alter table demo.membermaster-> add id int primary key auto_increment;
Query OK, 0 rows affected (0.09 sec)
Records: 0 Duplicates: 0 Warnings: 0
第三步,修改销售流水表,添加新的字段 memberid,对应会员信息表中的主键:
mysql> alter table demo.trans-> add memberid int;
Query OK, 0 rows affected (0.03 sec)
Records: 0 Duplicates: 0 Warnings: 0
第四步,更新一下销售流水表,给新添加的字段 memberid 赋值,让它指向对应的会员信息:
mysql> update demo.trans as a, demo.membermaster as b-> set a.memberid = b.id-> where a.transactionno > 0;-- > AND a.cardno = b.cardno; -- 这样操作可以不用删除trans的内容,在实际工作中更适合
Query OK, 1 row affected (0.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0
demo.membermaster 和 demo.trans 的结构:
mysql> describe demo.membermaster;
+---------------+----------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+----------+------+-----+---------+----------------+
| cardno | char(8) | NO | | NULL | |
| membername | text | YES | | NULL | |
| memberphone | text | YES | | NULL | |
| memberpid | text | YES | | NULL | |
| memberaddress | text | YES | | NULL | |
| sex | text | YES | | NULL | |
| birthday | datetime | YES | | NULL | |
| id | int | NO | PRI | NULL | auto_increment |
+---------------+----------+------+-----+---------+----------------+
8 rows in set (0.01 sec)mysql> describe demo.trans;
+---------------+---------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------------+---------------+------+-----+---------+-------+
| transactionno | int | YES | | NULL | |
| itemnumber | int | YES | | NULL | |
| quantity | decimal(10,3) | YES | | NULL | |
| price | decimal(10,2) | YES | | NULL | |
| salesvalue | decimal(10,2) | YES | | NULL | |
| cardno | char(8) | YES | | NULL | |
| transdate | datetime | YES | | NULL | |
| memberid | int | YES | | NULL | |
+---------------+---------------+------+-----+---------+-------+
8 rows in set (0.00 sec)
验证前,先将表中的 cardno 为 10000001 的用户改回张三:
mysql> select * from demo.membermaster;
+----------+------------+-------------+--------------------+---------------+------+---------------------+----+
| cardno | membername | memberphone | memberpid | memberaddress | sex | birthday | id |
+----------+------------+-------------+--------------------+---------------+------+---------------------+----+
| 10000001 | 张三 | 18758079160 | 110123200001017890 | 北京 | 男 | 2000-01-01 00:00:00 | 1 |
| 10000002 | 李四 | 18758079161 | 123123199001012356 | 上海 | 女 | 1990-01-01 00:00:00 | 2 |
+----------+------------+-------------+--------------------+---------------+------+---------------------+----+
如果张三的会员卡 10000001 不再使用,发给了王五,就在会员信息表里面增加一条记录:
mysql> insert into demo.membermaster-> (cardno, membername, memberphone, memberpid, memberaddress, sex, birthday)-> values-> ('10000001', '王五', '13698765432', '475145197001012356', '南京', '男', '1992-02-02');
Query OK, 1 row affected (0.00 sec)
查看所有会员信息:
mysql> select * from demo.membermaster;
+----------+------------+-------------+--------------------+---------------+------+---------------------+----+
| cardno | membername | memberphone | memberpid | memberaddress | sex | birthday | id |
+----------+------------+-------------+--------------------+---------------+------+---------------------+----+
| 10000001 | 张三 | 18758079160 | 110123200001017890 | 北京 | 男 | 2000-01-01 00:00:00 | 1 |
| 10000002 | 李四 | 18758079161 | 123123199001012356 | 上海 | 女 | 1990-01-01 00:00:00 | 2 |
| 10000001 | 王五 | 13698765432 | 475145197001012356 | 南京 | 男 | 1992-02-02 00:00:00 | 3 |
+----------+------------+-------------+--------------------+---------------+------+---------------------+----+
3 rows in set (0.00 sec)
由于字段 cardno 不再是主键,可以允许重复,因此,我们可以在保留会员“李四”信息的同时,添加使用同一会员卡号的“赵六”的信息。
现在再来查会员消费,就不会出问题了:
mysql> select b.membername, c.goodname, a.quantity, a.salesvalue, a.transdate-> from demo.trans as a-> join demo.membermaster as b-> join demo.goodsmaster as c-> on (a.memberid = b.id and a.itemnumber = c.itemnumber);
+------------+----------+----------+------------+---------------------+
| membername | goodname | quantity | salesvalue | transdate |
+------------+----------+----------+------------+---------------------+
| 张三 | book | 1.000 | 89.00 | 2020-12-01 00:00:00 |
+------------+----------+----------+------------+---------------------+
1 row in set (0.00 sec)
手动赋值字段做主键
为了解决这个问题,想了一个办法:取消字段“id”的自增属性,改成信息系统在添加会员的时候对“id”进行赋值。
具体的操作是这样的:在总部 MySQL 数据库中,有一个管理信息表,里面的信息包括成本核算策略,支付方式等,还有总部的系统参数,可以在这个表中添加一个字段,专门用来记录当前会员编号的最大值。
门店在添加会员的时候,先到总部 MySQL 数据库中获取这个最大值,在这个基础上加 1,然后用这个值作为新会员的“id”,同时,更新总部 MySQL 数据库管理信息表中的当前会员编号的最大值。这样一来,各个门店添加会员的时候,都对同一个总部 MySQL 数据库中的数据表字段进行操作,就解决了各门店添加会员时会员编号冲突的问题,同时也避免了使用业务字段导致数据错误的问题。
小结
- 用业务字段做主键,看起来很简单,但是应该尽量避免这样做。因为无法预测未来会不会因为业务需要,而出现业务字段重复或者重用的情况。
- 自增字段做主键,对于单机系统来说是没问题的。但是,如果有多台服务器,各自都可以录入数据,那就不一定适用了。因为如果每台机器各自产生的数据需要合并,就可能会出现主键重复的问题。
- 采用手动赋值的办法,通过一定的逻辑,确保字段值在全系统的唯一性,这样就可以规避主键重复的问题了。
刚开始使用 MySQL 时,很多人都很容易犯的错误是喜欢用业务字段做主键,想当然地认为了解业务需求,但实际情况往往出乎意料,而更改主键设置的成本非常高。所以,如果系统比较复杂,尽量给表加一个字段做主键,采用手动赋值的办法,虽然系统开发的时候麻烦一点,却可以避免后面出大问题。
给已有的字段增加主键:
alter table demo.importhead modify listnumber int primary key;
mysql> describe demo.importhead;
+----------------+---------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------------+---------------+------+-----+---------+-------+
| listnumber | int | YES | | NULL | |
| supplierid | int | YES | | NULL | |
| stocknumber | int | YES | | NULL | |
| importtype | int | YES | | 1 | |
| importquantity | decimal(10,3) | YES | | NULL | |
| importvalue | decimal(10,2) | YES | | NULL | |
| recorder | int | YES | | NULL | |
| recordingdate | datetime | YES | | NULL | |
+----------------+---------------+------+-----+---------+-------+
8 rows in set (0.00 sec)mysql> alter table demo.importhead modify listnumber int primary key;
Query OK, 0 rows affected (0.15 sec)
Records: 0 Duplicates: 0 Warnings: 0mysql> describe demo.importhead;
+----------------+---------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------------+---------------+------+-----+---------+-------+
| listnumber | int | NO | PRI | NULL | |
| supplierid | int | YES | | NULL | |
| stocknumber | int | YES | | NULL | |
| importtype | int | YES | | 1 | |
| importquantity | decimal(10,3) | YES | | NULL | |
| importvalue | decimal(10,2) | YES | | NULL | |
| recorder | int | YES | | NULL | |
| recordingdate | datetime | YES | | NULL | |
+----------------+---------------+------+-----+---------+-------+
8 rows in set (0.01 sec)
MySQL必知必会05:正确设置主键相关推荐
- MySQL 必知必会 四 (如何正确设置主键)
1.将数据表中已存在的主键删除 sql:alter table 数据表名 drop primary key; 2. 将数据表中已经存在的列设置为主键 sql : alter table 数据表名 ad ...
- mysql中用完即删用什么_MySQL使用和操作总结(《MySQL必知必会》读书笔记)
简介 MySQL是一种DBMS,即它是一种数据库软件.DBMS可分为两类:一类是基于共享文件系统的DBMS,另一类是基于客户机--服务器的DBMS.前者用于桌面用途,通常不用于高端或更关键应用. My ...
- MySQL必知必会笔记(一)基础知识和基本操作
第一章 了解MySQL 数据库 保存有组织的数据的容器.(通常是一个文件或一组文件) 人们经常使用数据库这个术语代替他们使用的软件.这是不正确的,确切的说,数据库软件应称为DBM ...
- 读书笔记系列1——MySQL必知必会
读书笔记系列1--MySQL必知必会 文章目录 读书笔记系列1--MySQL必知必会 MySQL官方文档:https://dev.mysql.com/doc/ 第一章 数据库基础 *2021.11.2 ...
- 30 分钟读完《MySQL 必知必会》
第一章 了解 SQL 介绍几个数据库的概念 数据库(database) 通过DBMS创建和操纵的保存有组织的数据的容器(通常是一个文件或一组文件). 数据库软件/数据库管理系统(DBMS)访问数据库, ...
- mysql必知必会_MySQL必知必会
MySQL必知必会 联结的使用, 子查询, 正则表达式和基于全文本的搜索, 存储过程, 游标, 触发器, 表约束. 了解SQL 数据库基础 电子邮件地址薄里查找名字时, 因特网搜索站点上进行搜索, 验 ...
- mysql必知必会_5天学完《MySQL必知必会》学习笔记之第四天
本篇知识点 更新删除数据.创建操纵表.视图.存储过程 更新和删除数据 使用UPDATE语句更新(修改)表中的数据: 更新表中特定行(使用WHERE语句 更新表中所有行 UPDATE语句以要更新的表名开 ...
- mysql日期维表sql文件_《MySQL必知必会》笔记(SQL练习+建表语句)
站在巨人的肩上 Standing On Shoulders Of Giants 部分转自:https://www.jianshu.com/p/294502893128 https://blog.csd ...
- 《MySQL必知必会学习笔记》:更新和删除数据
更新和删除数据 上篇博文介绍了下插入数据的几种方式.这篇博文就来讲述下更新和删除数据. 更新数据 更新(修改)表中的数据,使用update语句. 更新数据一般采用如下两种方式: 更新表中特定行的数据. ...
最新文章
- maven工程插件配置
- 设计模式笔记之十四 (命令模式)
- 成本直降50% | 阿里云发布云原生网关,开启下一代网关新进程
- 01: MySql简介
- 【虚拟化实战】存储设计之一存储类型
- 开发一个简单的WebPart
- Eclipse如何导入第三方jar包
- IOS10.8.2安装
- 语言栈十进制转十六进制_十进制数转二进制、八进制、十六进制等任意进制_C语言「抄作业」...
- 【转贴】没有div没有float没有clear没有hack的超强CSS布局
- java集合学习笔记--二维集合HashMap
- DICOM医学图像简介
- (executor 1 exited caused by one of the running tasks) Reason: Executor heartbeat timed out after
- vmware反复触发numlock问题
- 【最后有彩蛋】Pyhton接口测试手册
- 能够边下边看bt资源的工具:Tribler Mac中文免费版
- java 高德地图工具类
- 【转】欧盟物联网研究战略路线图(3)
- Fresh gizmo
- 终于搞定Paypal了
热门文章
- VxLAN数据中心L2/L3互通(端到端)
- 大数据24小时:美团收购摩拜被证实;武汉一小学用大数据监测是否走神
- php 内容写入文件内容_PHP使用内置函数file_put_contents写入文件及追加内容的方法...
- Java项目中如何找control层,再问一个,业务层和MVC中Controler(控制器)的关系解决方法...
- 16 声音检测算法的封装
- 计算机操作系统感悟随笔--进程通信
- 关键是你的心里要有阳光
- C#底层库--万能进制转换器(自定义有序字符,支持任意进制)
- 机器人钩阿木木_英雄联盟:机器人最不敢钩的英雄,石头算一个,钩中他坐等团灭!...
- Fetch()方法介绍