上一篇 42 篇把建表语句的骨架(create table + 字段 + 约束 + 注释)搭好了,这一篇专讲骨架里紧跟字段名的那一样东西——数据类型。下一篇 44 篇就会拿"员工表"把约束和类型合起来用一遍。
IMPORTANT
一句话说清这一篇的位置:约束管"这个字段的值必须满足什么条件",类型管"这个字段存的是什么、能存多大"。两者写在同一个字段定义里,位置是 字段名 类型 [约束] [comment 注释]。
PPT 第 33 页只写了一句概述:
MySQL 中的数据类型有很多,主要分为三类:数值类型、字符串类型、日期时间类型。
详细的三张表格来自课程配套的 资料/04. 数据类型/MySQL数据类型.xlsx(PPT 上没有铺开),下面按数值、字符串、日期时间三张表逐类抄录,并配上本机实测。
数值类型(8 种)
三种"整数值"和三种"小数"要分开看:tinyint / smallint / mediumint / int / bigint 是整数,float / double 是浮点数(近似值),decimal 是定点数(精确值)。
| 类型 | 大小(byte) | 有符号范围 | 无符号范围 | 描述 | 备注 |
|---|---|---|---|---|---|
| tinyint | 1 | (-128,127) | (0,255) | 小整数值 | 字段值很小时用它,如性别、职位 |
| smallint | 2 | (-32768,32767) | (0,65535) | 大整数值 | — |
| mediumint | 3 | (-8388608,8388607) | (0,16777215) | 大整数值 | — |
| int | 4 | (-2147483648,2147483647) | (0,4294967295) | 大整数值 | 最常用的整数类型 |
| bigint | 8 | (-2^63,2^63-1) | (0,2^64-1) | 极大整数值 | 超出 int 范围时用 |
| float | 4 | (-3.402823466 E+38,3.402823466351 E+38) | 0 和 (1.175494351 E-38,3.402823466 E+38) | 单精度浮点数值 | float(5,2):5 = 整个数字长度,2 = 小数位个数;存的是近似值 |
| double | 8 | (-1.7976931348623157 E+308,1.7976931348623157 E+308) | 0 和 (2.2250738585072014 E-308,1.7976931348623157 E+308) | 双精度浮点数值 | double(5,2) 同上,精度比 float 高,但仍然是近似值 |
| decimal | 由精度决定 | decimal(5,2):5 = 整个数字长度,2 = 小数位个数 | 同左(精度写法一致) | 精确小数值(金额用它) | xlsx 里对 decimal 只给了精度写法,范围由精度决定 |
三个"取值范围"要能对上号,比如常用的这几个:
| 类型 | 占几个字节 | 有符号能存到 | 加了 unsigned 能存到 |
|---|---|---|---|
tinyint | 1 | -128 ~ 127 | 0 ~ 255 |
int | 4 | 约 -21 亿 ~ 21 亿 | 0 ~ 约 42 亿 |
bigint | 8 | 约 -9.2×10^18 ~ 9.2×10^18 | 0 ~ 约 1.8×10^19 |
unsigned 是什么意思(PPT 第 34 页问题一)
PPT 第 34 页的问答原文:
问:数值类型在定义的时候,后面加了 unsigned 关键字是什么意思? 答:unsigned 表示无符号类型,表示只能取 0 及正数;不加默认是 signed,表示可以取负数。
"符号"指的就是正负号。加上 unsigned 之后,原来用来表示负数的那一半取值空间全部挪给正数,所以上限直接翻倍:tinyint 从 -128~127 变成 0~255,int 从约 ±21 亿变成 0~约 42 亿。
TIP
实测(本机 MySQL 9.0.1):把 tinyint unsigned 类型的字段分别塞进 -1 和 256,两个都插不进去(无符号的上限是 255)——
ERROR 1264 (22003): Out of range value for column 'tiny' at row 1报错原文里的 Out of range value(值超出范围) 就是"越界",1264 这个错误码后面还会遇到(凡是插入的值超出字段类型能表示的范围,基本都是它)。
什么时候该加 unsigned?规律很简单——这个字段在业务上不可能为负:年龄、薪资、性别编号、职位编号、学号……课程里员工表的写法就是现成例子:
-- 课程 SQL脚本.sql 中 emp 表的片段
id int unsigned primary key auto_increment comment 'ID, 主键',
gender tinyint unsigned not null comment '性别, 1 男; 2 女',
job tinyint unsigned comment '职位, 1 班主任; 2 讲师; 3 学工主管; 4 教研主管; 5 咨询师',
salary int unsigned comment '薪资',id、gender、job、salary 都不会是负数,所以统一加了 unsigned;其中 gender、job 的取值只有几个,用 tinyint 就够,没必要上 int。
选类型的原则:够用就好
xlsx 上给出的选取原则原话是:
在满足业务需求的前提下,尽可能选择占用磁盘空间小的数据类型。
所以它给的示例字段是 age tinyint unsigned(年龄最大也就 100 多,1 个字节足够)、id int unsigned(表里的数据量用 4 个字节足够装下)。反过来,"为了保险全用 bigint"是典型的浪费——字段越宽,占的磁盘、读写时搬的数据都更多。
float / double 与 decimal:小数为什么还有两种
这是最容易踩的一个坑,先用实测说话:
TIP
实测(本机 MySQL 9.0.1):
-- float 字段存 1234567.89
select ... ; -- 查出来是 1234570 ← 精度丢了
-- decimal(10,2) 字段存 1234567.89
select ... ; -- 查出来是 1234567.89 ← 一分不差
-- decimal(10,2) 字段存 1.999
select ... ; -- 查出来是 2.00 ← 小数位四舍五入,而且不报错这就是"金额用 decimal"的实证:float / double 是二进制近似存储,位数一多就不精确(存进去 1234567.89,取出来变成 1234570);decimal 按十进制精确存储,写多少是多少,小数位超出时四舍五入而不是报错(1.999 在 2 位小数上就是 2.00)。
| 类型 | 存法 | 精度 | 该不该用来存金额 |
|---|---|---|---|
float | 二进制近似 | 约 7 位有效数字 | 不行(实测 1234567.89 → 1234570) |
double | 二进制近似 | 约 15 位有效数字 | 不建议(仍然是近似值) |
decimal(M,D) | 十进制精确 | M 位数字、D 位小数 | 可以,金额就选它 |
字符串类型(10 种)
字符串类型里最常用的是 char 和 varchar,另外还有两组"长文本 / 二进制"类型(text / blob 系列)备用:
| 类型 | 大小 | 描述 | 备注(xlsx 原话与课程示例字段) |
|---|---|---|---|
| char | 0-255 bytes | 定长字符串 | char(10) 固定占用 10 个字符空间:存 A 占 10,存 ABC 占 10;优势:性能略高;劣势:浪费磁盘空间。示例:idcard char(18)、phone char(11) |
| varchar | 0-65535 bytes | 变长字符串 | varchar(10) 最多占用 10 个字符空间:存 A 占 1,存 ABC 占 3;优势:节约磁盘空间;劣势:性能略低。示例:username varchar(50) |
| tinyblob | 0-255 bytes | 不超过 255 个字符的二进制数据 | 存二进制(图片、文件字节)用 |
| tinytext | 0-255 bytes | 短文本字符串 | — |
| blob | 0-65 535 bytes | 二进制形式的长文本数据 | — |
| text | 0-65 535 bytes | 长文本数据 | 存长文本用 |
| mediumblob | 0-16 777 215 bytes | 二进制形式的中等长度文本数据 | — |
| mediumtext | 0-16 777 215 bytes | 中等长度文本数据 | — |
| longblob | 0-4 294 967 295 bytes | 二进制形式的极大文本数据 | — |
| longtext | 0-4 294 967 295 bytes | 极大文本数据 | — |
注意 char(n) / varchar(n) 括号里的 n 是"字符数"不是字节数(这是 MySQL 的写法,不是所有数据库都一样),所以 phone char(11) 正好装下一个 11 位手机号。
char 与 varchar 的区别(PPT 第 34 页问题二)
PPT 第 34 页的问答原文:
问:char 与 varchar 的区别是什么?什么时候用 char,什么时候用 varchar? 答:char 是定长字符串,varchar 是变长字符串。 如果一个字段的长度是固定的,建议使用 char;如:身份证号、手机号。 如果一个字段的长度不是固定的,建议使用 varchar;如:用户名、姓名。
为什么长度固定时 char 反而更好?xlsx 把账算得很清楚:
| 对比项 | char(10) | varchar(10) |
|---|---|---|
| 定义 | 定长字符串 | 变长字符串 |
存 'A' 占几个字符 | 10(后面自动补空格,凑满长度) | 1(只占实际长度) |
存 'ABC' 占几个字符 | 10 | 3 |
| 优势 | 性能略高(长度固定,存取定位快) | 节约磁盘空间(用多少占多少) |
| 劣势 | 浪费磁盘空间 | 性能略低 |
WARNING
"char 会补空格"这件事在查询结果里看不出来。实测(本机 MySQL 9.0.1):char(11) 和 varchar(11) 两个字段都存 'ab',用 char_length 查出来的长度都是 2——char 尾部补的那些空格在检索时会被自动去掉。所以别指望用查询结果去验证"它到底是定长还是变长",两者的差别在存储层面(占了多少空间、读写快不快),只能靠定义去判断。
字符串类型也有"越界"
只要值的长度超过括号里允许的范围,插入就会失败:
TIP
实测(本机 MySQL 9.0.1):往 varchar(10) 的字段里塞 11 个字符——
ERROR 1406 (22001): Data too long for column 'title' at row 1Data too long(数据太长) 是字符串类型专属的越界报错,错误码 1406。它提醒我们:varchar(10) 里的 10 是上限,写需求时要把"最长的可能值"留出余量(比如用户名 varchar(50)、姓名 varchar(10))。
日期时间类型(5 种)
| 类型 | 大小(byte) | 范围 | 格式 | 描述 |
|---|---|---|---|---|
| date | 3 | 1000-01-01 至 9999-12-31 | YYYY-MM-DD | 日期值 |
| time | 3 | -838:59:59 至 838:59:59 | HH:MM:SS | 时间值或持续时间 |
| year | 1 | 1901 至 2155 | YYYY | 年份值 |
| datetime | 8 | 1000-01-01 00:00:00 至 9999-12-31 23:59:59 | YYYY-MM-DD HH:MM:SS | 混合日期和时间值 |
| timestamp | 4 | 1970-01-01 00:00:01 至 2038-01-19 03:14:07 | YYYY-MM-DD HH:MM:SS | 混合日期和时间值,时间戳 |
选用的规律:
| 要存的东西 | 用哪个类型 | 例子 |
|---|---|---|
| 只要"哪一天" | date | 入职日期 entry_date date(xlsx 示例 birthday date) |
| 要"哪一天几点几分几秒" | datetime | 创建时间 / 修改时间(xlsx 示例 operateTime datetime) |
| 只要"哪一年" | year | 入学年份 |
| 只要"时分秒"或"一段时长" | time | 视频时长 |
datetime 和 timestamp 都是"日期 + 时间",格式也一样,区别在范围:timestamp 只到 2038-01-19(4 个字节存的时间戳,也叫"2038 问题"),而 datetime 能存到 9999 年。表里的业务时间(入职时间、下单时间)一般用 datetime,7 个字节的余量换来了省心。
日期写错了会怎样
TIP
实测(本机 MySQL 9.0.1):
-- 用斜杠写日期,居然也能插进去
... values ('2020/01/01') -- 插入成功,存成了 2020-01-01
-- 月份写成 13
... values ('2020-13-01') -- ERROR 1292 (22007): Incorrect date value: '2020-13-01' for column 'birth' at row 1结论有两条:① 格式可以宽松(2020/01/01 这种斜线写法 MySQL 也认);② 值不能乱来(月份 13 不存在,直接报 1292 Incorrect date value)。所以日期字段插不进去时,先看值本身是不是一个真实存在的日期。
小结
| 问题 | 答案 |
|---|---|
| 数据类型分几类 | 三类:数值类型、字符串类型、日期时间类型(PPT 第 33 页) |
| 整数怎么选 | 在够用的前提下选小的:tinyint(1 字节,-128~127 / 0~255)、smallint、mediumint、int(4 字节)、bigint(8 字节);表里的 id 用 int unsigned,性别/职位这种小取值范围用 tinyint unsigned |
unsigned 是什么 | 无符号类型,只能取 0 及正数;不加默认是 signed(可以有负数)。加上之后上限翻倍(如 tinyint 127 → 255)。存了超范围的值报 1264 Out of range value |
| 小数怎么选 | 一般计算用 float / double(近似值);金额一定用 decimal(精确值,decimal(5,2) 的 5 = 总长度、2 = 小数位)。实测 float 存 1234567.89 变成 1234570,而 decimal(10,2) 分毫不差 |
char 与 varchar 的区别 | char 定长:char(10) 存 A 也占 10 个字符,优势是性能略高、劣势是浪费空间;varchar 变长:varchar(10) 存 A 只占 1,优势是省空间、劣势是性能略低 |
| 什么时候用哪个 | 长度固定用 char(身份证号 char(18)、手机号 char(11));长度不固定用 varchar(用户名 varchar(50)、姓名 varchar(10)) |
| 字符串越界报什么 | 超过长度上限报 1406 Data too long for column |
| 日期时间怎么选 | 只要日期用 date(YYYY-MM-DD)、日期时间都要用 datetime(YYYY-MM-DD HH:MM:SS)、只要年份用 year、只要时间/时长用 time;timestamp 范围只到 2038 年 |
| 日期插不进去 | 值不合法报 1292 Incorrect date value(如 2020-13-01);斜杠格式 '2020/01/01' 是能插入的 |
| 选类型的总原则 | 在满足业务需求的前提下,尽可能选择占用磁盘空间小的数据类型(够用就好) |
相关
练习题
一、知识回顾(读完直接做下面的实践题)
- 数据类型分三类:数值类型、字符串类型、日期时间类型(PPT 第 33 页只有这一句概述,三张详细表格来自课程配套的
MySQL数据类型.xlsx) - 整数类型按占用空间从小到大:
tinyint(1 字节,有符号 -128~127 / 无符号 0~255)、smallint(2 字节)、mediumint(3 字节)、int(4 字节,约 ±21 亿 / 无符号约 42 亿)、bigint(8 字节) unsigned表示无符号类型,只能取 0 及正数;不加默认是signed(可以取负数)。加unsigned后上限翻倍(tinyint:127 → 255)。插入超出范围的值报ERROR 1264 (22003): Out of range value for column ...- 小数类型:
float(4 字节,单精度近似值)、double(8 字节,双精度近似值)、decimal(精确小数值,金额用);decimal(5,2)里的 5 = 整个数字长度、2 = 小数位个数 - 实测证据:
float字段存 1234567.89 查出来是 1234570(精度丢失);decimal(10,2)存同一个数分毫不差;decimal(10,2)存 1.999 得到 2.00(小数位四舍五入,不报错)——所以金额字段必须用decimal char是定长字符串(char(10)存 A 占 10 个字符,优势性能略高、劣势浪费空间);varchar是变长字符串(varchar(10)存 A 只占 1 个字符,优势节约空间、劣势性能略低)- 选择规律:长度固定的字段用
char——身份证号char(18)、手机号char(11);长度不固定的字段用varchar——用户名varchar(50)、姓名varchar(10)。char补的空格在检索时会被去掉,所以实测char(11)和varchar(11)存'ab'查出来的char_length都是 2(差别在存储层面) - 字符串类型越界报
ERROR 1406 (22001): Data too long for column 'xxx' at row 1(如varchar(10)里塞 11 个字符);长文本用text系列,二进制数据用blob系列 - 日期时间类型:
date(3 字节,YYYY-MM-DD)、time(3 字节,HH:MM:SS)、year(1 字节,YYYY)、datetime(8 字节,YYYY-MM-DD HH:MM:SS)、timestamp(4 字节,时间戳,范围只到 2038-01-19);只要日期用date,日期时间都要用datetime - 日期实测:
'2020/01/01'这种斜杠写法能插入(MySQL 接受),但值不合法(如'2020-13-01')报ERROR 1292 (22007): Incorrect date value - 选类型的总原则(xlsx 原话):在满足业务需求的前提下,尽可能选择占用磁盘空间小的数据类型——比如年龄用
age tinyint unsigned、id用int unsigned
二、裸写题
字段 用途说明 id 每条商品的编号,做唯一标识,从 1 开始自动往后发,不会是负数 name 商品名称,最长 50 个字符 price 售价,要求精确到分 stock 库存数量,最大几万件,不会是负数 is_on_sale 是否上架,只有"上架 / 下架"两种 pic_path 商品图片的存放路径,最长 255 个字符 detail 商品详情,可能是很长的一段文字 create_time 商品上架的具体日期和时间(精确到秒) - 提示(先自己想,实在想不出再点开)
一级 · 思路:先给每个字段归到"数值 / 字符串 / 日期时间"三大类里的哪一类,再在同一类里挑"装得下、又不浪费"的那一个——数量只有几种的字段别用
int,固定长度的字符串想想要不要定长,金额要精确 二级 · 方法:整数用tinyint/smallint/int(不会为负就加unsigned,但id做标识时还要能自动发号);金额用decimal(总长度,小数位);字符串长度固定用char(n)、不固定用varchar(n)、很长用text;日期时间用date/datetime三级 · 骨架:id ____ unsigned primary key auto_increment、is_on_sale ____ unsigned、price ____(10,2)、name ____(50)、detail ____、create_time ____- 参考答案(做完再点开)
字段 类型 理由 id int unsigned标识用整数,业务上不为负;表里数据量用 4 个字节足够(够用就好) name varchar(50)商品名称长度不固定,用变长字符串省空间 price decimal(10,2)金额必须用精确类型,2 位小数精确到分(实测 float存 1234567.89 会变成 1234570)stock int unsigned库存是整数且不为负;几万件用 int足够(也可以mediumint unsigned)is_on_sale tinyint unsigned只有两种取值(1 上架 / 2 下架),1 个字节就够,范围 0~255 pic_path varchar(255)路径长度不固定,上限给到 255(课程 emp 表的 image varchar(255)就是这么写的)detail text可能是很长的一段文字 → 用长文本类型 text(xlsx 里text的定位就是"长文本数据",0-65 535 bytes)create_time datetime日期和时间都要,格式 YYYY-MM-DD HH:MM:SS 一句总原则收尾:能满足需求的前提下选占用空间小的类型——所以这里没有一处用
bigint或double。- 身份证号(固定 18 位)
- 手机号(固定 11 位)
- 姓名(1~10 个字不等)
- 用户名(3~20 个字符不等)
- 学号(固定 10 位数字)
做完再想一个"反例":如果把手机号写成
varchar(11),功能上还能存下 11 位手机号吗?那为什么课程里还是用char(11)?- 提示(先自己想,实在想不出再点开)
一级 · 思路:只看一件事——这个字段的长度是不是"每个值都一样"。长度固定的用定长字符串(省得每次算长度,性能略高);长度会变的用变长字符串(省磁盘) 二级 · 方法:定长用
char(n),变长用varchar(n),括号里的 n 是字符数上限三级 · 骨架:身份证号____(18)、手机号____(11)、姓名____(10)、用户名____(20)、学号____(10)- 参考答案(做完再点开)
- 身份证号 →
char(18):每个人的身份证号都是 18 位,长度固定(xlsx 备注里的示例字段就是idcard char(18)) - 手机号 →
char(11):固定 11 位(xlsx 备注示例phone char(11)) - 姓名 →
varchar(10):1 个字到十几个字都有,长度不固定(PPT 第 34 页举的例子) - 用户名 →
varchar(50):长度不固定,短的 3 个字符、长的十几个(课程 emp 表用的是varchar(20),user表用的是varchar(50)) - 学号 →
char(10):固定 10 位(真实业务里如果学号长度不固定,就该改成varchar)
反例的答案:写
varchar(11)功能上也能存下 11 位手机号,但长度固定的字段用定长字符串性能略高,而且char能顺带表达"这一定是个 11 位的值"这层含义,所以课程里选char(11)。反过来,如果长度不固定的字段用了char,每行都要按最大长度占空间,浪费磁盘。- sql
create table score( id int unsigned primary key auto_increment comment 'ID', age tinyint unsigned comment '年龄', salary int unsigned comment '薪资' ); insert into score(age) values (-1); -- ERROR 1264 (22003): Out of range value for column 'age' at row 1- 三个字段都写了
unsigned,它是什么意思?不加的话默认是什么? - 为什么插入
-1会报错?这个错误码是多少、报错原文说的是什么? - 同样的
tinyint,加了unsigned之后能存的最大值是多少?不加呢?
- 提示(先自己想,实在想不出再点开)
一级 · 思路:这题的关键词是"符号"——
unsigned决定这个数字字段能不能是负数,以及正数那一半的空间有多大二级 · 方法:unsigned= 无符号(只能 0 和正数);不加默认signed= 有符号(可以取负数);越界报错的关键词是Out of range value三级 · 骨架:tinyint有符号范围-128 ~ ____,无符号范围0 ~ ____- 参考答案(做完再点开)
unsigned表示无符号类型,表示只能取 0 及正数;不加默认是signed(有符号,可以取负数)。年龄、薪资这类字段业务上不可能为负,加上unsigned顺带把上限翻倍。- 因为
age是tinyint unsigned,取值范围是 0~255,-1落在这个范围之外,所以越界报错——ERROR 1264 (22003): Out of range value for column 'age' at row 1,关键词就是Out of range value(值超出范围)。 tinyint不加unsigned的范围是 -128~127;加了unsigned是 0~255(原来表示负数的那一半空间挪给了正数,所以能存的最大值从 127 变成 255)。
顺带一提:本机实测同一个错误在
tinyint unsigned字段里塞256时也会报ERROR 1264——两头越界都是同一个错误码。 - 三个字段都写了
现象 相关字段定义 报错 / 表现 现象 1 nickname varchar(2)存 3 个字的昵称时报 ERROR 1406 (22001): Data too long for column 'nickname' at row 1现象 2 birth date存 '2020-13-01'时报ERROR 1292 (22007): Incorrect date value: '2020-13-01' for column 'birth' at row 1现象 3 tuition float存 1234567.89不报错,但查出来变成1234570现象 4 score decimal(10,2)存 1.999不报错,但查出来变成2.00- 提示(先自己想,实在想不出再点开)
一级 · 思路:四个现象分两类——前两个是"值不符合类型的限制"(会报错),后两个是"类型的精度特性"(不报错,但数据被改了)。改法都是从"这个字段真正要存什么"出发 二级 · 方法:字符串超长要么把长度上限开大(
varchar(n)的 n),要么改用能装长文本的类型;日期字段的值必须是真实存在的日期(月份只能是 1~12);货币金额不要用浮点类型,要用精确类型decimal(总长度,小数位);decimal小数位超出时会四舍五入三级 · 骨架:nickname ____(20)、birth ____、tuition ____(10,2)- 参考答案(做完再点开)
- 现象 1(
1406 Data too long):varchar(2)只允许 2 个字符,3 个字的昵称超长。改法:把上限开大,例如nickname varchar(20)。"数据太长"(Data too long)就是字符串类型的越界报错。 - 现象 2(
1292 Incorrect date value):'2020-13-01'里的月份 13 不存在,不是一个合法日期。改法:把值写成真实存在的日期(如'2020-12-01');顺带说一句,实测'2020/01/01'这种斜杠格式反而能插入成功——格式宽松,但值必须合法。 - 现象 3(
float丢精度):float是近似的单精度浮点数,位数一多就不准,实测存 1234567.89 查出来是 1234570。改法:金额字段改成decimal(10,2)——实测同一个数用decimal(10,2)存,查出来就是 1234567.89,分毫不差。 - 现象 4(
decimal四舍五入):这其实不是 bug。decimal(10,2)只保留 2 位小数,多出来的第三位自动四舍五入,所以 1.999 变成 2.00,而且不报错。要接受"以为存了 1.999,其实存的是 2.00"这件事——需要更高精度就把小数位开大(如decimal(10,3))。
一句话总结这题:越界和非法值会报错(1264 / 1406 / 1292),精度问题不报错但会悄悄改数据(float 丢精度、decimal 四舍五入)——后者更危险,所以类型要在一开始就选对。
- 现象 1(
三、综合题
- 每个学员有一个编号,做唯一标识,从 1 开始自动往后发,不会是负数;
- 学员姓名最长 10 个字;
- 性别只有"男 / 女"两种(用 1、2 表示);
- 手机号固定 11 位;
- 身份证号固定 18 位;
- 入学日期只要"哪一天";结课时间要精确到秒;
- 学费要精确到分;
- 照片存的是文件路径(最长 255 个字符);
- 备注可能是一段很长的文字。
要求:① 先列出"字段名 → 类型 → 为什么选它"的清单(9 个字段都要);② 把清单落成一条完整的
create table语句(字段都加comment);③ 建完表之后,故意做两个实验:往手机号字段里塞 12 位数字、往学费字段里塞一个带 3 位小数的数,把两次的结果(成功/报错原文)记在练习文件里。涉及知识点
知识点 在这里的应用 数值类型与 unsigned 学员编号用 int unsigned;性别用tinyint unsigned(只有两种取值,够用就好)decimal 学费精确到分,用 decimal(10,2),不能用 float/doublechar / varchar 手机号、身份证号长度固定 → char;姓名、图片路径长度不固定 →varchartext 备注是很长的文字 → text日期时间类型 入学日期只要日期 → date;结课时间要日期时间 →datetime选取原则 全程遵循"满足需求的前提下选占用空间小的类型" - 提示(先自己想,实在想不出再点开)
一级 · 思路:先把 9 个需求各自翻译成"字段名 + 一句话的存储要求",再按三大类(数值 / 字符串 / 日期时间)归类;归类之后只在同类里挑最小的那一个,注意四个"特殊要求"——编号不为负、性别取值极少、金额要精确、固定长度的字符串二级 · 方法:整数
int unsigned/tinyint unsigned;精确小数decimal(10,2);定长字符串char(n)、变长varchar(n)、长文本text;只存日期date、存到秒datetime;建表时字段后面可以跟comment '注释'三级 · 骨架:create table student( id ____ unsigned primary key auto_increment comment 'ID', name ____(10) comment '姓名', gender ____ unsigned comment '性别', phone ____(11) comment '手机号', idcard ____(18) comment '身份证号', entry_date ____ comment '入学日期', graduate_time ____ comment '结课时间', tuition ____(10,2) comment '学费', image ____(255) comment '照片', remark ____ comment '备注' ) comment '学员表';- 参考答案(做完再点开)
① 字段清单:
字段 类型 为什么 id int unsigned编号是整数、不为负,4 个字节够用 name varchar(10)姓名 1~10 个字,长度不固定 gender tinyint unsigned只有两种取值,1 个字节足够(1 男 / 2 女) phone char(11)手机号长度固定 11 位 idcard char(18)身份证号长度固定 18 位 entry_date date只要"哪一天" graduate_time datetime要精确到秒 tuition decimal(10,2)金额必须精确,2 位小数精确到分 image varchar(255)路径长度不固定,上限 255 remark text长文本 ② 建表语句:
sqlcreate table student( id int unsigned primary key auto_increment comment 'ID, 主键', name varchar(10) not null comment '姓名', gender tinyint unsigned not null comment '性别, 1 男; 2 女', phone char(11) not null comment '手机号', idcard char(18) comment '身份证号', entry_date date comment '入学日期', graduate_time datetime comment '结课时间', tuition decimal(10,2) comment '学费', image varchar(255) comment '照片路径', remark text comment '备注' ) comment '学员表';③ 两个实验的结果(本机实测,MySQL 9.0.1):
- 往
char(11)的手机号里塞 12 位数字 → 报错,关键词是Data too long(ERROR 1406 (22001))——字符串类型装不下就报这个; - 往
decimal(10,2)的学费里塞带 3 位小数的数(如1.999)→ 不报错,值被四舍五入成 2.00。这说明 decimal 会"悄悄改数",写业务代码时要么保证小数位不超、要么接受舍入。
顺带对照一下:如果这里把学费写成
float,就不会有四舍五入,而是更麻烦的精度丢失——本机实测float存1234567.89查出来是1234570。这就是"金额用 decimal"的实证。
