一、先看全貌:类型分几大类
数据类型
├── 数值类型
│ ├── 整型 tinyint / smallint / mediumint / int / bigint
│ ├── 位类型 bit
│ └── 小数 float / double / decimal
├── 字符串类型
│ ├── char / varchar
│ ├── text / blob
│ └── enum / set ← 单选 / 多选
└── 日期时间类型
├── date
├── datetime
└── timestamp
为什么非要选类型? 就像买收纳盒:
- 你说"我要一个格子",店家会问"放戒指还是放被子?"------大小不一样,价格不一样;
- 数据库里,类型决定了:占多少空间 、能存多大范围 、能不能比较/排序 、会不会偷偷四舍五入。
选错类型的典型后果:
- 用
tinyint存手机号 → 存不下,报错; - 用
float存金额 → 0.1 + 0.2 不等于 0.3,账算不平; - 用
char(50)存地址 → 每条记录浪费几十字节,一百万条就是几十 MB 白扔。
二、整型:tinyint 与 unsigned
2.1 先做个实验:越界会怎样
mysql> create table tt1(num tinyint);
Query OK, 0 rows affected (0.02 sec)
mysql> insert into tt1 values(1);
Query OK, 1 row affected (0.00 sec)
mysql> insert into tt1 values(128); -- 越界插入,报错
ERROR 1264 (22003): Out of range value for column 'num' at row 1
mysql> select * from tt1;
+------+
| num |
+------+
| 1 |
+------+
1 row in set (0.00 sec)
发生了什么 :tinyint 的范围是 -128 ~ 127 ,你偏要塞 128,MySQL 直接拒绝(报 Out of range),原来那行 1 还在。
📌 注意是报错不是"截断"。这条数据压根没进去。
2.2 有符号 vs 无符号
课件说明:
在 MySQL 中,整型可以指定是有符号 的和无符号 的,默认是有符号的 。可以通过
UNSIGNED来说明某个字段是无符号的。
| 类型 | 有符号范围(默认) | 无符号范围(unsigned) |
|---|---|---|
tinyint |
-128 ~ 127 | 0 ~ 255 |
smallint |
-32768 ~ 32767 | 0 ~ 65535 |
int |
-21 亿 ~ 21 亿 | 0 ~ 42 亿 |
bigint |
极大 | 极大 |
打个比方:
- 有符号 = 温度计,能让零下;
- 无符号 = 年龄、库存、价格,永远不可能是负数,所以把"负数那半边"也拿来做正数,能存的范围翻倍。
2.3 无符号案例
mysql> create table tt2(num tinyint unsigned);
Query OK, 0 rows affected (0.02 sec)
mysql> insert into tt2 values(-1); -- 无符号,范围是:0 - 255
ERROR 1264 (22003): Out of range value for column 'num' at row 1
mysql> insert into tt2 values(255);
Query OK, 1 row affected (0.02 sec)
mysql> select * from tt2;
+------+
| num |
+------+
| 255 |
+------+
1 row in set (0.00 sec)
- 插
-1→ 报错(无符号不收负数); - 插
255→ 成功(上限从 127 提到 255)。
2.4 课件的一条重要建议(反直觉)
⚠️ 注意:尽量不使用 unsigned ,对于 int 类型可能存放不下的数据,
int unsigned同样可能存放不下。与其如此,还不如设计时,将 int 类型提升为 bigint 类型。
为什么这么说? 因为 unsigned 会带来两个麻烦:
- 范围只翻了一倍 ,治标不治本。真正的风险是"以后数据会超过 int 上限",那就直接用
bigint; - 做减法时会出错 :
unsigned的列减一个更大的数,MySQL 会报错或绕回成一个巨大的正数(0 - 1变成 42 亿),这是非常隐蔽的 bug。
打个比方 :你的车油箱不够用,unsigned 相当于"把座椅拆了装个副油箱"(只多一点,还影响别的功能);而换 bigint 相当于"直接换个大油箱"(一步解决)。
📌 实战建议:能用
int就用int;可能超大(比如日志 ID、时间戳)用bigint;unsigned少用。
三、bit:位类型(存开关和布尔值)
3.1 语法
bit[(M)]
M表示每个值的位数,范围从 1 到 64 。如果 M 被忽略,默认为 1。
3.2 一个"诡异"的案例
mysql> create table tt4 (id int, a bit(8));
Query OK, 0 rows affected (0.01 sec)
mysql> insert into tt4 values(10, 10);
Query OK, 1 row affected (0.01 sec)
mysql> select * from tt4; # 发现很怪异的现象,a 的数据 10 没有出现
+------+------+
| id | a |
+------+------+
| 10 | |
+------+------+
1 row in set (0.00 sec)
为什么看不到 10? 因为:
bit 字段在显示时,是按照 ASCII 码对应的值显示。
ASCII 码 10 是"换行符"(不可见字符),所以显示成空白。
再插一个 65:
mysql> insert into tt4 values(65, 65);
mysql> select * from tt4;
+------+------+
| id | a |
+------+------+
| 10 | |
| 65 | A |
+------+------+
2 rows in set (0.00 sec)
ASCII 65 就是大写字母 A,所以显示 A。
📌 结论 :
bit显示的是"这个数字对应的字符",不是数字本身。想看数字得用a+0或bin(a)之类的转换。
3.3 什么时候用 bit
如果我们有这样的值,只存放 0 或 1 ,这时可以定义
bit(1)。这样可以节省空间。
比如:
- 性别(0/1)
- 是否删除(0/1)
- 是否已读(0/1)
一个 bit(1) 只占 1 位,而 tinyint 要占 1 字节(8 位)。八倍差距,数据量大时很可观。
打个比方 :bit(1) 是一个电灯开关,只有开/关两种状态;tinyint 是一个抽屉,能装 -128~127 但你就放个 0 或 1,浪费。
⚠️ 不过实务中很多人还是用
tinyint(1)当布尔值,因为 ORM/各种工具对bit的支持不一定完美。这一点知道就行。
四、小数类型:float 与 decimal
小数是最容易出错的一块,因为涉及精度。
4.1 float 语法
float[(m, d)] [unsigned]
M指定显示长度 ,d指定小数位数 ,占用空间 4 个字节。
怎么理解 m 和 d?
float(4,2):
d=2→ 小数点后 2 位;m=4→ 总共 4 位(整数部分 + 小数点 + 小数部分里,MySQL 里 m 指的是总位数,不含小数点);- 所以范围是 -99.99 ~ 99.99。
4.2 案例:范围和四舍五入
mysql> create table tt6(id int, salary float(4,2));
Query OK, 0 rows affected (0.01 sec)
mysql> insert into tt6 values(100, -99.99);
Query OK, 1 row affected (0.00 sec)
mysql> insert into tt6 values(101, -99.991); # 多的这一点被拿掉了
Query OK, 1 row affected (0.00 sec)
mysql> select * from tt6;
+------+--------+
| id | salary |
+------+--------+
| 100 | -99.99 |
| 101 | -99.99 |
+------+--------+
2 rows in set (0.00 sec)
两个现象:
-99.991被存成-99.99------多的小数位被截掉/四舍五入;- 范围就是
-99.99 ~ 99.99,超出就报错或警告。
课件留了个思考题:
float(6,3)的范围是多少? 答案:整数部分 3 位、小数 3 位 → -999.999 ~ 999.999。
4.3 无符号的 float
mysql> create table tt7(id int, salary float(4,2) unsigned);
Query OK, 0 rows affected (0.01 sec)
mysql> insert into tt7 values(100, -0.1);
Query OK, 1 row affected, 1 warning (0.00 sec)
mysql> show warnings;
+---------+------+-------------------------------------------------+
| Level | Code | Message |
+---------+------+-------------------------------------------------+
| Warning | 1264 | Out of range value for column 'salary' at row 1 |
+---------+------+-------------------------------------------------+
1 row in set (0.00 sec)
mysql> insert into tt7 values(100, -0);
Query OK, 1 row affected (0.00 sec)
mysql> insert into tt7 values(100, 99.99);
Query OK, 1 row affected (0.00 sec)
注意这里的细节:插 -0.1 时不是报错而是一条 Warning ,值被存成了 0.00(因为无符号范围是 0~99.99)。
📌
-0是合法的(它等于 0),但-0.1越界。
4.4 decimal:精度更高的定点数
decimal(m, d) [unsigned]
decimal(5,2)表示的范围是 -999.99 ~ 999.99 ;decimal(5,2) unsigned表示范围 0 ~ 999.99。
看起来和 float 一模一样,但关键区别在精度:
decimal整数最大位数m为 65 ,支持小数最大位数d是 30 。 如果d被省略,默认为 0;如果m被省略,默认是 10。说明:
float表示的精度大约是 7 位。
4.5 精度对比实验(本篇最值得记住的)
mysql> create table tt8 ( id int, salary float(10,8), salary2 decimal(10,8));
Query OK, 0 rows affected (0.01 sec)
mysql> insert into tt8 values(100, 23.12345612, 23.12345612);
Query OK, 1 row affected (0.00 sec)
mysql> select * from tt8;
+------+-------------+-------------+
| id | salary | salary2 |
+------+-------------+-------------+
| 100 | 23.12345695 | 23.12345612 |
+------+-------------+-------------+
1 row in set (0.00 sec)
同样的输入 23.12345612:
float存成了23.12345695(后面几位全乱了);decimal原封不动23.12345612。
为什么? 因为 float 是二进制浮点数 ,很多十进制小数在二进制里是"除不尽的无限循环小数",只能近似。而 decimal 是按十进制存的,能精确表示。
📌 课件建议:如果希望小数的精度高,推荐使用
decimal。
4.6 什么时候用哪个
| 场景 | 用什么 | 原因 |
|---|---|---|
| 金额、价格、工资、账目 | decimal |
一分钱都不能差 |
| 科学计算、坐标、图形 | float / double |
只要近似值,且运算快、省空间 |
| 百分比、评分(如 4.5 分) | decimal(3,1) |
精度够、可读性好 |
打个比方:
float是一把弹簧秤,读数快、范围大,但"1.000 克"可能显示成"1.002 克";decimal是一台精密天平,慢一点、占地方,但你说 23.12345612 它就记 23.12345612。
⚠️ 血泪教训 :很多系统用
float存金额,结果对账差几分钱,查半天查不出来。金额永远用 decimal。
五、字符串类型:char 与 varchar
这两个是新手最容易混的,我们从"它俩到底差在哪"讲起。
5.1 char:定长字符串
char(L)
char(L):固定长度字符串,L是可以存储的长度,单位为字符,最大长度值可以为 255。
关键词:固定长度 、单位是"字符" (不是字节)、最大 255。
案例:
mysql> create table tt9(id int, name char(2));
Query OK, 0 rows affected (0.00 sec)
mysql> insert into tt9 values(100, 'ab');
Query OK, 1 row affected (0.00 sec)
mysql> insert into tt9 values(101, '中国');
Query OK, 1 row affected (0.00 sec)
mysql> select * from tt9;
+------+--------+
| id | name |
+------+--------+
| 100 | ab |
| 101 | 中国 |
+------+--------+
2 rows in set (0.00 sec)
char(2)表示可以存放两个字符 ,可以是字母或汉字,但是不能超过 2 个,最多只能是 255。
注意 :char(2) 写 '中国' 是可以的------因为 L 的单位是"字符"不是"字节",两个汉字就是两个字符。
超过 255 会怎样:
mysql> create table tt10(id int ,name char(256));
ERROR 1074 (42000): Column length too big for column 'name' (max = 255); use BLOB or TEXT instead
报错直接告诉你:max = 255,想存更长请用 BLOB / TEXT。
5.2 varchar:变长字符串
varchar(L)
varchar(L):可变长度字符串,L表示字符长度,最大长度 65535 个字节。
注意这里的措辞:最大 65535 个字节------不是字符!这就是要命的地方。
课件专门解释了:
关于
varchar(len),len到底是多大,这个len值和表的编码 密切相关:varchar长度可以指定为 0 到 65535 之间的值,但是有 1 - 3 个字节用于记录数据大小 ,所以说有效字节数是 65532。
- 当表的编码是 utf8 时,
varchar(n)的参数 n 最大值是 65532 / 3 = 21844 (因为 utf8 中一个字符占用 3 个字节);- 如果编码是 gbk ,
varchar(n)的参数 n 最大是 65532 / 2 = 32766 (因为 gbk 中一个字符占用 2 字节)。
翻译成一张表:
| 编码 | 一个字符占几字节 | varchar(n) 的 n 上限 |
|---|---|---|
utf8 |
3 | 21844 |
gbk |
2 | 32766 |
验证实验:
mysql> create table tt11(name varchar(21845)) charset=utf8; -- 验证了 utf8 确实不能超过 21844
ERROR 1118 (42000): Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. You have to change some columns to TEXT or BLOBs
mysql> create table tt11(name varchar(21844)) charset=utf8;
Query OK, 0 rows affected (0.01 sec)
21845 报错、21844 通过,刚好卡在 21844。
为什么? 因为 21844 × 3 = 65532(减去记录长度的 1-3 字节),再加上其他开销就超过 65535 的单行上限了。
5.3 案例:varchar 能存多少就看定义
mysql> create table tt10(id int ,name varchar(6)); -- 表示这里可以存放 6 个字符
mysql> insert into tt10 values(100, 'hello');
mysql> insert into tt10 values(100, '我爱你,中国');
mysql> select * from tt10;
+------+--------------------+
| id | name |
+------+--------------------+
| 100 | hello |
| 100 | 我爱你,中国 |
+------+--------------------+
2 rows in set (0.00 sec)
varchar(6) 存 '我爱你,中国'------数一下字符:我(1)爱(2)你(3),(4)中(5)国(6),正好 6 个字符,所以能存。
📌 再次强调 :
varchar(6)的 6 是字符数,不是字节数。中文也算 1 个字符。
5.4 char vs varchar:到底差在哪
| 对比项 | char(L) |
varchar(L) |
|---|---|---|
| 长度 | 固定 L 个字符 | 可变,最多 L 个字符 |
| 单位 | 字符 | 字符(但底层按字节算上限) |
| 最大 | 255 个字符 | utf8 下 21844 、gbk 下 32766 |
| 空间 | 总是占用 L 个字符的空间(不够会补空格) | 按实际长度占用 + 1-2 字节记录长度 |
| 效率 | 高(长度固定,定位快) | 稍低(要先读长度) |
| 适合 | 长度固定的数据 | 长度不定的数据 |
课件对选择的总结非常到位:
如何选择定长或变长字符串?
如果数据确定长度都一样 ,就使用定长(char) ,比如:身份证、手机号、md5
如果数据长度有变化 ,就使用变长(varchar) ,比如:名字、地址,但是你要保证最长的能存的进去。
定长的磁盘空间比较浪费,但是效率高。
变长的磁盘空间比较节省,但是效率低。
定长的意义是:直接开辟好对应的空间。
变长的意义是:在不超过自定义范围的情况下,用多少,开辟多少。
打个比方:
char(10)像一排固定 10 个格子的收纳盒。你放 3 个东西,剩下 7 个格子空着,但盒子大小不变;varchar(10)像一个能撑到 10 格的弹性袋子。放 3 个就只占 3 格,但袋子本身要记一下"我现在装了 3 格"(那 1-2 字节) 。
5.5 实战选择建议
| 数据 | 选什么 | 理由 |
|---|---|---|
| 身份证号 | char(18) |
固定 18 位 |
| 手机号 | char(11) |
固定 11 位 |
| 密码 md5 | char(32) |
固定 32 位 |
| 姓名 | varchar(20) |
有人 2 字有人 20 字 |
| 地址 | varchar(200) |
长短差别巨大 |
| 邮箱 | varchar(100) |
长度不定 |
| 文章正文 | text |
超过 varchar 上限 |
六、日期和时间类型
6.1 三个常用类型
| 类型 | 格式 | 范围 | 占用 |
|---|---|---|---|
date |
'yyyy-mm-dd' |
日期 | 3 字节 |
datetime |
'yyyy-mm-dd HH:ii:ss' |
1000 ~ 9999 年 | 8 字节 |
timestamp |
时间戳(从 1970 年开始),格式与 datetime 完全一致 | 1970 ~ 2038 | 4 字节 |
6.2 关键区别:timestamp 会"自动干活"
mysql> create table birthday (t1 date, t2 datetime, t3 timestamp);
Query OK, 0 rows affected (0.01 sec)
-- 只插前两列,第三列不填
mysql> insert into birthday(t1,t2) values('1997-7-1','2008-8-8 12:1:1');
Query OK, 1 row affected (0.00 sec)
mysql> select * from birthday;
+------------+---------------------+---------------------+
| t1 | t2 | t3 |
+------------+---------------------+---------------------+
| 1997-07-01 | 2008-08-08 12:01:01 | 2017-11-12 18:28:55 |
+------------+---------------------+---------------------+
1 row in set (0.00 sec)
注意
t3(timestamp)那一列:添加数据时,时间戳自动补上当前时间(虽然我们没给它赋值)。
再看更新:
mysql> update birthday set t1='2000-1-1';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select * from birthday;
+------------+---------------------+---------------------+
| t1 | t2 | t3 |
+------------+---------------------+---------------------+
| 2000-01-01 | 2008-08-08 12:01:01 | 2017-11-12 18:32:09 |
+------------+---------------------+---------------------+
1 row in set (0.00 sec)
更新数据时,时间戳会更新成当前时间 (对比一下:
18:28:55→18:32:09,变了)。
而 t2(datetime)纹丝不动。
6.3 三个类型的适用场景
| 类型 | 适合 | 特点 |
|---|---|---|
date |
生日、入职日期 | 只要日期,不要时分秒 |
datetime |
订单创建时间、预约时间 | 需要精确到秒,且不会自己变 |
timestamp |
created_at、updated_at |
省空间(4字节)、自动维护 ,但2038 年就溢出了 |
打个比方:
datetime是你手写在纸上的时间,写多少就是多少,不会自己变;timestamp是一块自动盖章的电子钟,你不填它自己盖当前时间,你每次改这张纸它又盖一次。
6.4 ⚠️ 2038 问题
timestamp 占 4 字节,最大只能表示到 2038-01-19 03:14:07(因为 32 位有符号整数溢出了)。
📌 现在很多新项目直接用
datetime或bigint(存毫秒时间戳)来避开这个坑。老系统里这个雷是真实存在的。
七、enum 和 set:单选与多选
这两个很像,区别就一个:能选几个。
7.1 enum:单选
enum('选项1','选项2','选项3',...)
enum:枚举,"单选"类型。 该设定只是提供了若干个选项的值,最终一个单元格中,实际只存储了其中一个值 ;而且出于效率考虑,这些值实际存储的是"数字" ------这些选项的每个选项值依次对应如下数字:1,2,3,... ,最多 65535 个。 当我们添加枚举值时,也可以添加对应的数字编号。
打个比方 :这就是一道单选题,A/B/C/D 只能选一个;而且系统内部把 A 记作 1、B 记作 2......
7.2 set:多选
set('选项值1','选项值2','选项值3', ...)
set:集合,"多选"类型。 该设定只是提供了若干个选项的值,最终一个单元格中,可以存储其中任意多个值 ;而且出于效率考虑,这些值实际存储的是"数字" ------每个选项值依次对应:1,2,4,8,16,32,...,最多 64 个。
注意数字规律 :enum 是 1,2,3,4(连续),set 是 1,2,4,8,16(2 的幂)!
为什么?因为 set 要用二进制位表示"选了哪几个":
| 选项 | 对应位 | 数字 |
|---|---|---|
| 登山 | 第 1 位 | 1 |
| 游泳 | 第 2 位 | 2 |
| 篮球 | 第 3 位 | 4 |
| 武术 | 第 4 位 | 8 |
选了"登山+武术" → 1 + 8 = 9(二进制 1001)。一个数字就表达了多选组合,非常省空间。
📌 课件提到"想想 Linux 权限"------没错,这和 Linux 的 755 权限(r=4, w=2, x=1 相加)是同一个思路。
7.3 完整案例
mysql> create table votes(
-> username varchar(30),
-> hobby set('登山','游泳','篮球','武术'), -- 注意:使用数字标识每个爱好时,想想 Linux 权限,采用比特位位置
-> gender enum('男','女')); -- 注意:使用数字标识的时候,就是正常的数组下标
Query OK, 0 rows affected (0.02 sec)
插入数据:
insert into votes values('雷锋', '登山,武术', '男');
insert into votes values('Juse','登山,武术',2); -- gender 用数字 2 表示"女"
查询:
mysql> select * from votes where gender=2;
+----------+---------------+--------+
| username | hobby | gender |
+----------+---------------+--------+
| Juse | 登山,武术 |女 |
+----------+---------------+--------+
用数字
2也能匹配到enum的第二个值"女"。
7.4 一个重要建议
说明:不建议在添加枚举值、集合值的时候采用数字的方式,因为不利于阅读。
理由很简单:三个月后你看到 gender=2,得回去数"男是1、女是2",还不如直接看 gender='女' 清楚。
7.5 set 的查询坑(重点)
有一个调查表,数据如下:
+-----------+---------------+--------+
| username | hobby | gender |
+-----------+---------------+--------+
| 雷锋 | 登山,武术 | 男 |
| Juse | 登山,武术 | 女 |
| LiLei | 登山 | 男 |
| LiLei | 篮球 | 男 |
| HanMeiMei | 游泳 | 女 |
+-----------+---------------+--------+
错误写法:直接等于
mysql> select * from votes where hobby='登山';
+----------+--------+--------+
| username | hobby | gender |
+----------+--------+--------+
| LiLei | 登山 | 男 |
+----------+--------+--------+
不能查询出所有爱好为登山的人! 因为它只匹配"hobby 的值恰好等于 '登山'"的那一行,而
'登山,武术'不是'登山'。
正确写法 :用 find_in_set
find_in_set(sub, str_list)
如果
sub在str_list中,则返回下标 ;如果不在,返回 0 ;str_list是用逗号分隔的字符串。
先验证函数行为:
mysql> select find_in_set('a', 'a,b,c');
+---------------------------+
| find_in_set('a', 'a,b,c') |
+---------------------------+
| 1 |
+---------------------------+
mysql> select find_in_set('d', 'a,b,c');
+---------------------------+
| find_in_set('d', 'a,b,c') |
+---------------------------+
| 0 |
+---------------------------+
再查:
mysql> select * from votes where find_in_set('登山', hobby);
+----------+---------------+--------+
| username | hobby | gender |
+----------+---------------+--------+
| 雷锋 | 登山,武术 | 男 |
| Juse | 登山,武术 | 女 |
| LiLei | 登山 | 男 |
+----------+---------------+--------+
三行全出来了,这才是想要的结果。
打个比方 :hobby='登山' 是"你的购物清单必须只有 登山这一项";find_in_set('登山', hobby) 是"你的购物清单里包含登山"。前者太严格,后者才是你要的。
八、一张表总结所有类型
| 大类 | 类型 | 大小/范围 | 典型用途 | 一句话 |
|---|---|---|---|---|
| 整型 | tinyint |
-128127 / 0255 | 年龄、状态码 | 小数字 |
int |
±21 亿 | ID、数量 | 最常用 | |
bigint |
极大 | 大数据量 ID、时间戳 | 装不下就用它 | |
| 位 | bit(M) |
1~64 位 | 布尔开关 | 按 ASCII 显示 |
| 小数 | float(m,d) |
4 字节,精度约 7 位 | 坐标、评分 | 快但不准 |
decimal(m,d) |
精度 m≤65、d≤30 | 金额 | 准但稍慢 | |
| 字符串 | char(L) |
≤255 字符,定长 | 身份证、手机号、md5 | 固定格子 |
varchar(L) |
utf8 下 ≤21844 字符,变长 | 姓名、地址、邮箱 | 弹性袋子 | |
text |
更大 | 文章正文 | 大文本 | |
| 时间 | date |
3 字节 | 生日 | 只要日期 |
datetime |
8 字节 | 订单时间 | 手写时间 | |
timestamp |
4 字节 | created_at | 自动盖章,2038 溢出 | |
| 选择 | enum |
最多 65535 个 | 性别、状态 | 单选 |
set |
最多 64 个 | 爱好、标签 | 多选(位运算) |
九、易错点速记
| 误解 | 正确理解 |
|---|---|
| 整型默认无符号 | 默认有符号 ,无符号要写 unsigned |
| 越界会截断 | 大多直接报错 (Out of range) |
bit 会显示数字 |
显示的是 ASCII 字符 (65 显示成 A) |
float(4,2) 表示 4 位整数 |
表示总共 4 位,范围 -99.99~99.99 |
| float 和 decimal 差不多 | 精度差很多,金额必须用 decimal |
varchar(10) 能存 10 个字节 |
是 10 个字符,utf8 下要占 30 字节 |
varchar 上限 65535 |
utf8 下实际只能 21844,gbk 下 32766 |
char(2) 存不下"中国" |
能存,单位是字符不是字节 |
| timestamp 和 datetime 一样 | timestamp 自动补/更新时间,且 2038 溢出 |
hobby='登山' 能查所有喜欢登山的人 |
不能,要用 find_in_set('登山', hobby) |
| enum 的数字是 1,2,4,8 | enum 是 1,2,3,4 ;set 才是 1,2,4,8 |
十、本篇小结
- 类型决定空间、范围、精度,选错会报错或者算错账。
- 整型默认有符号;
unsigned只是翻倍,长得用好bigint,别迷信 unsigned。 bit省空间但显示成 ASCII 字符,适合 0/1 开关。float不准,decimal准 ------金额一律decimal。char定长(≤255 字符,效率高但浪费空间),varchar变长(utf8 下 ≤21844 字符,省空间但稍慢);身份证/手机号/md5 用 char,姓名/地址用 varchar。date/datetime/timestamp按需选;timestamp会自动维护但有 2038 问题。enum单选、set多选;set 的数字是 2 的幂(位运算),查询多选值要用find_in_set。