MySQL 学习(4)常用数据类型

🔥keyipatience:个人主页

🎬作者简介:C/C++后端开发学习者

🌟专栏传送门:《c++》《linux》《c++高阶数据结构》《c++数据结构与算法》《前端学习》

《Git》《Mysql》

⭐️patience is key in life

一、数据类型分类

分类 常见类型 常见用途
整数 tinyint、`smallint`、`mediumint`、`int`、`bigint` 状态、数量、编号
小数 float、`double`、`decimal` 测量值、金额
位类型 bit 位标记、二进制位值
字符串 char、`varchar`、`text` 编码、姓名、文本
日期和时间 date、`datetime`、`timestamp` 日期、业务时间、时间戳
枚举和集合 enum、`set` 固定选项、多个选项

二、数值类型

1. 整数类型

类型 存储空间 有符号范围 unsigned 范围
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 字节 -9223372036854775808 到 9223372036854775807 0 到 18446744073709551615

日常开发最常用:int、bigint 主键 id 现在很多项目推荐 bigint,防止 int 上限不够

语法 demo

复制代码
drop table if exists test_num;
create table test_num (
    id int primary key auto_increment,
    age tinyint,                -- 有符号,可以存负数
    score tinyint unsigned,     -- 无符号,不能负数,0~255
    num bigint unsigned
);

insert into test_num(age,score,num) values (18,99,100000);
-- insert into test_num(age,score) values (-5,-10);  -- score 无符号,-10 报错

细节:

1:以前 int(11)、tinyint(3) 这种括号里的数字:显示宽度,和存储范围完全无关 只是客户端查询展示的时候,不够位数补空格(配合 zerofill 才生效),mysql8.0 已经不推荐写。 int(11) 照样可以存 2147483647,不是最多只能存 11 位数字

复制代码
-- zerofill 示例,了解即可,项目几乎不用
create table t_int_demo(
  num int(5) zerofill
);
insert into t_int_demo values(12);
-- 查询出来展示:00012

2:unsigned 只是去掉负数,最大值翻倍

比如 tinyint unsigned:0~255,适合年龄、分数、数量这种不会负的数据。

3:auto_increment 配合 unsigned

复制代码
id int unsigned primary key auto_increment

无符号自增 id,可用 id 数量更多。

选型建议

  • 年龄、状态标记:tinyint
  • 普通 id:int
  • 大数据量、订单 id、分布式主键:bigint

2. 小数类型

类型 字节 特点 使用场景
float 4 单精度浮点,精度低,有误差 温度、权重、不需要精确的科学数据
double 8 双精度浮点,精度比 float 高,依然有误差 统计、科学计算,不涉及金额
decimal(m,d) 变长 定点数,精确无误差 金额、价格、财务(最重要!)

decimal (m,d)

  • m:总位数(整数 + 小数加起来)最大65

  • d:小数部分位数 最大30

  • 例:decimal(10,2) 最多一共 10 位,小数占 2 位 → 整数最多 8 位 范围:-99999999.99 ~ 99999999.99

    drop table if exists test_float;
    create table test_float(
    id int primary key auto_increment,
    f float,
    d double,
    price decimal(10,2)
    );

    insert into test_float(f,d,price) values (0.1,0.1,0.1);
    select * from test_float;

    -- 重点:浮点相加,会出现精度bug
    select f + 0.2, d + 0.2, price + 0.2 from test_float;

    • 0.1 + 0.2
      float、double 可能算出 0.30000000000000004 这种,不准
    • decimal 算出来稳稳 0.30

商品价格、订单金额等字段通常使用 decimal。定义精度时要留足整数位和小数位,避免金额增长后超过字段范围。

mysql 8.0.17 起,float(m, d)`和 double(m, d) 这种指定总位数和小数位数的写法已弃用。新表通常直接声明 float 或 double;需要精确小数位时使用 decimal(p, s)。

3. bit

bit(m) 用于保存位值,m`表示位数,范围为 1 到 64。例如 bit(8) 可以表示 8 位的二进制值。

小 demo

复制代码
create table demo_bit (
  id int primary key auto_increment,
  flag bit(1),
  bits3 bit(3)
) default character set utf8mb4;

插入写法,两种方式:

复制代码
-- 方式1:b'二进制串'
insert into demo_bit(flag,bits3) values (b'1', b'101');
-- 方式2:直接写数字(会自动转二进制)
insert into demo_bit(flag,bits3) values (0, 5);

查询的时候注意: 直接 select 看到的是二进制原始字节,不会显示 0/1 ,要用 bin() 函数转成二进制字符串看:

复制代码
select id, bin(flag), bin(bits3) from demo_bit;

输出:

三、字符串类型

1.char (m) 固定长度

  • m:最大字符数,范围 1 ~ 255

  • 存储规则:不管你数据写多长,固定占用 m 个字符对应的字节空间 数据不够 m 个字符 → 末尾自动补空格填充; 查询读取的时候,mysql默认把末尾的空格删掉。

    -- demo
    create table demo_char (
    phone char(11) comment '手机号固定11位'
    ) default character set utf8mb4;
    insert into demo_char(phone) values ('13800138000');

示例:char(11),存'13800138000',utf8mb4 下固定占 11*4=44 字节,就算只存 5 个字符,依然占 44 字节。

优点:长度固定,mysql 定位数据很快,索引性能更好

缺点:短数据浪费存储空间

适合:长度固定不变的数据:手机号、身份证号、验证码、编码

大坑:末尾空格会被吃掉

复制代码
-- char(4) 存入 'ab  '(ab后面两个空格)
-- select 拿出来得到 'ab'
-- where 'ab' = 'ab ' 判定相等!空格不参与比对

2. varchar (m) 可变长度(业务最常用)

  • m:允许的最大字符数, 65535 字节(行限制)即最大字符数65535 / 4 ≈ 16383。
  • 存储规则:占用字节 = 真实数据字节 + 长度标记
    • 数据字节 ≤255:长度标记占 1 字节

    • 数据字节 >255:长度标记占 2 字节

      -- demo
      create table demo_varchar (
      name varchar(20) comment '姓名,长短不一定'
      ) default character set utf8mb4;
      insert into demo_varchar(name) values ('张三');

示例:varchar(20),存入张三,utf8mb4:2 个中文占 6 字节 + 1 字节长度标记,一共 7 字节。

优点:省空间

缺点:需要记录长度,读取速度略慢于 char

适合:长短不固定:姓名、地址、标题、备注

注意:

char(11)、varchar(11) 括号里的数字,代表字符数量,不是字节数量

utf8mb4 里不同字符占用的字节不一样:

  • 英文字母 a:1 个字符,占 1 字节

  • 中文 你:1 个字符,占 3 字节

  • emoji 😀:1 个字符,占 4 字节

    name varchar(2);

含义:最多允许存 2 个字符,不是 2 个字节。 可以存入:

  • ab → 2 字符,一共 2 字节
  • 小明 → 2 字符,一共 6 字节
  • 😀a → 2 字符,一共 4+1=5 字节

这三条都合法,都满足「最多 2 个字符」的限制。

整张行所有字段加起来总字节不能超过 65535 字节,是行上限,不是单个varchar字段。

这是 InnoDB 行的最大行大小限制 ,MySQL 层的定义:一行记录(除去 text、blob 这类溢出字段)的总存储字节上限是 65535 字节(2¹⁶−1)

text / tinytext / blob 这类大字段不计入这个行长度 它们在本行里面只存一个很小的指针(20 字节左右),真实数据放到行外溢出页,所以不受这个 65535 限制。

比如下面两个字段,单独看都不大,加起来就炸:

复制代码
create table test_sum(
  a varchar(10000),
  b varchar(10000)
) default character set utf8mb4;

utf8mb4,最坏情况:10000**4 + 10000**4 = 80000 >65535 → 建表报错。

3.text 系列

表格(长度前缀 + 最大容量,单位:字节)

表格

类型 长度前缀 最大容量
tinytext 1 字节 255 B
text 2 字节 65535 B(≈64KB)
mediumtext 3 字节 16777215 B(≈16MB)
longtext 4 字节 4294967295 B(≈4GB)

基础语法 demo

复制代码
create table demo_text (
  id int primary key auto_increment,
  t1 tinytext,
  t2 text,
  t3 mediumtext,
  t4 longtext
) default character set utf8mb4;

insert into demo_text(t1, t2) values
('简短备注', '一大段文章正文......');

重点特性

  1. 不能像 varchar 那样,用括号限制存储上限 text(200) 语法允许写,但不是限制最多存 200 字符 ,只是客户端展示宽度,没有存储约束,开发不要这么写。varchar(200) 的 200 是硬上限,这是巨大区别。
  2. 不计入 65535 行大小限制(InnoDB) 普通字段(char、varchar、int)的最大字节总和不能超过 65535; text 在本行里面只存一个很小的溢出指针,真正文本数据放到单独的溢出页。 所以存很长的内容,选 text,绕开 65535 的限制。 代价:读取的时候,需要额外一次 IO 读取溢出页,性能比 varchar 差。

四.日期时间类型

一共 5 种:date、time、datetime、timestamp、year

表格

类型 占用字节 格式 取值范围 核心特点
date 3 YYYY-MM-DD 1000-01-01 ~ 9999-12-31 只存日期,没有时分秒
time 3 HH:MM:SS -838:59:59 ~ 838:59:59 可以表示时长,支持负数、大于 24 小时
datetime 8 YYYY-MM-DD HH:MM:SS 1000-01-01 00:00:00 ~ 9999-12-31 23:59:59 日期 + 时间,不做时区转换
timestamp 4 YYYY-MM-DD HH:MM:SS 1970-01-01 00:00:01 UTC ~ 2038-01-19 03:14:07 UTC 底层 UTC 时间,读写自动时区转换,存在 2038 溢出问题

1. date

只存年月日,没有时分秒。 demo:

复制代码
create table demo_date (
  id int primary key auto_increment,
  birthday date
);
insert into demo_date(birthday) values ('2026-05-20');

适用:生日、下单日期,不需要几点几分。

2. time

不只是一天内的时刻,用来记录时间间隔。 可以是负数、超过 24 小时。 demo:

复制代码
create table demo_time (
  id int primary key auto_increment,
  spend_time time
);
insert into demo_time(spend_time) values ('25:00:00'), ('-01:30:00');

适用:视频时长、耗时。

3. datetime

保存的就是你看到的那个年月日时分秒文本,不带时区换算,时区改了,时间数字不变。

datetime 执行插入:

复制代码
insert into t (dt) values ('2026-11-04 21:00:00');

硬盘里面老老实实存的就是一串字面:2026-11-04 21:00:00,就算时区改了也是这个值

常见用法:

复制代码
create_time datetime default current_timestamp,
update_time datetime default current_timestamp on update current_timestamp
//`on update`:**当这条记录被更新的时候触发**。

1.create_time:记录创建时间

  • 新增一行的时候,你不填这个字段,mysql 自动填上现在的时间
  • 之后不管你怎么改这条数据,它永远不变

2.update_time:记录最后修改时间

  • 新增一行的时候,不填,自动填现在的时间
  • 只要你修改这一行任意别的字段,它自动刷新成刚刚修改的时间
  • 如果你 update 了,但是数据和原来一模一样,它不会变

这个语法 MySQL5.6 及以后才支持 datetime 用 on update;更早版本只有 timestamp 支持。

完整 demo,直接复制运行

复制代码
-- 如果表存在就删掉
drop table if exists test_time;

-- 建表
create table test_time (
    id int primary key auto_increment,
    name varchar(20),
    create_time datetime default current_timestamp,
    update_time datetime default current_timestamp on update current_timestamp
);

-- 插入数据,我们不写时间字段,让它自动生成
insert into test_time(name) values ('张三');

-- 查询,看两个时间是一样的
select * from test_time;

插入结果示例:

表格

id name create_time update_time
1 张三 2026-11-04 17:30:00 2026-11-04 17:30:00
复制代码
-- 更新 name,触发 on update
update test_time set name = '张三改名字了' where id=1;

-- 再次查询
select * from test_time;

更新后结果:

表格

id name create_time update_time
1 张三改名字了 2026-11-04 17:30:00 2026-11-04 17:32:10

看到区别: create_time 保持最开始插入的时间不变 update_time 自动变成刚刚更新的时间

4. timestamp

底层存 UTC 时间戳,读写的时候自动根据当前会话时区转换。

复制代码
create table demo_ts (
  id int primary key auto_increment,
  ts timestamp default current_timestamp on update current_timestamp
);

大坑: 上限到 2038-01-19,时间到了之后溢出报错;修改数据库时区,查询出来的时间跟着变。新项目尽量不用。

区分重点: datetime:存什么,查出来就是什么,时区改了不受影响。 timestamp:存 UTC,展示的时候换算成当前时区。

MySQL8.0 默认开启了 explicit_defaults_for_timestamp=ON,不再自动给 timestamp 加上 ON UPDATE CURRENT_TIMESTAMP!即更新的时候时间戳不会自动补上当前时间,必须手写声明,才会更新!

复制代码
t3 timestamp default current_timestamp on update current_timestamp

五、enum和 set

enum 与 set 都是字符串类型,但它们的取值范围由建表时定义的选项决定。

1. enum 枚举(单选)

语法:enum('值1','值2','值3') 含义:只能选里面其中一个,不能多个

复制代码
drop table if exists test_enum;
create table test_enum (
    id int primary key auto_increment,
    gender enum('男','女','保密')
);

-- ✅ 合法
insert into test_enum(gender) values ('男');
insert into test_enum(gender) values ('保密');

-- ❌ 非法,不在列表里,默认会变成空字符串(严格模式下直接报错)
-- insert into test_enum(gender) values ('外星人');

select * from test_enum;

特点:

  • 单选,只能存一个
  • 底层存数字:'男'=1,'女'=2,'保密'=3,节省空间
  • 适合:性别、状态(启用 / 禁用)

2. set 集合(多选)

语法:set('值1','值2','值3') 含义:可以选一个或者多个,多个用逗号隔开,不能有空格

复制代码
drop table if exists test_set;
create table test_set (
    id int primary key auto_increment,
    hobby set('篮球','游戏','读书','跑步')
);

-- ✅ 选1个
insert into test_set(hobby) values ('游戏');
-- ✅ 选多个!逗号分隔,中间不要空格
insert into test_set(hobby) values ('游戏,读书');
insert into test_set(hobby) values ('篮球,跑步,读书');

-- ❌ 错误写法(逗号后面带空格)
-- insert into test_set(hobby) values ('游戏, 读书');

select * from test_set;

特点:

  • 多选,最多可以组合里面任意几项
  • 底层用位运算存储,非常省空间
  • 适合:爱好、标签、权限

3. 对比表

类型 选择 存储 典型场景
enum 只能单选 数字 性别、订单状态
set 支持多选 位图 爱好、标签、权限

4. 重要坑点

  1. set 多个值中间逗号不能加空格 '游戏,读书' ✅ | '游戏, 读书' ❌
  2. 严格模式 sql_mode='STRICT_TRANS_TABLES' 下,传非法值直接报错;非严格模式会截断 / 存空
  3. 不推荐大量业务使用:
    • 新增选项需要 alter table 修改字段,改动麻烦
    • 程序里不好维护,一般开发中更常用单独字典表代替 enum /set

5. 查询小 demo(set 专属)

find_in_set() 专门查 set 里面有没有某个值

复制代码
-- 查出爱好包含读书的行
select * from test_set where find_in_set('读书', hobby);
--只是读书
select * from test_set where hobby = '读书';
相关推荐
达梦产品与服务24 分钟前
SQLark V4.2 更新 | 新增TiDB、SSL安全连接、数据字典、清空/截断表等40+项功能已上线!
数据库·安全·tidb·ssl
码农阿豪1 小时前
SQL Server数据迁移完成后,凌晨的数据库告警该怎么查?
数据库·sqlserver
bjzhang751 小时前
sqlsugar入门
数据库·sqlsugar
全栈陈序员3 小时前
【Git】从入门到精通(十)—— 撤销与恢复:git checkout与git restore
运维·网络·数据库·git
ss27310 小时前
为什么PostgreSQL适合AI场景?
数据库·人工智能·postgresql
瑞克-桑切斯10 小时前
7、MySQL 慢查询排查与优化实战指南
java·服务器·数据库
z06.11 小时前
MySQL索引
数据库·mysql
考虑考虑11 小时前
Posrgresql恢复.backup文件
数据库·后端·postgresql
云祺vinchin11 小时前
中小企业备份软件选型从恢复需求到总体成本评估
数据库·数据备份·中小企业备份·容灾备份软件