MySQL数据类型怎么选?整型、字符串、日期等全面解析
作者:吹什么轩
一、先看全貌:类型分几大类
数据类型
├── 数值类型
│ ├── 整型 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。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
