从之前的SQL练习中看到存储不同的数据需要不同的数据类型,那这些字段该如何选择类型呢?
目标
- 了解不同类型以及使用场景
1字符串类型
1.1CHAR 和 VARCHAR
char:用来保存固定长度的字符,使用时后面通常会声明固定的长度,范围是0~255。比如char(10)表示最多可以容纳10个字符。
varchar:可变长度,范围是0~65535。
通过实例验证两种类型区别:
1. 创建库表
mysql> create database test1;
Query OK, 1 row affected (0.02 sec)
mysql> use test1
Database changed
mysql> create table tbl_1(one char(10), two varchar(10));
Query OK, 0 rows affected (0.03 sec)
#2. 各自写入超过10个字符的数据
mysql> insert into tbl_1 (one) values ('abcabcabcabc');
ERROR 1406 (22001): Data too long for column 'one' at row 1
mysql>
mysql> insert into tbl_1 (two) values ('abcabcabcabc');
ERROR 1406 (22001): Data too long for column 'two' at row 1
mysql>
会发现两条语句都不能插入数据。
3. 写入少于10个字符的数据
mysql> insert into tbl_1 (one) values ('abc');
Query OK, 1 row affected (0.02 sec)
mysql> insert into tbl_1 (two) values ('abc');
Query OK, 1 row affected (0.00 sec)
mysql> select * from tbl_1;
+------+------+
| one | two |
+------+------+
| abc | NULL |
| NULL | abc |
+------+------+
2 rows in set (0.01 sec)
4. 写入值携带空格
mysql> insert into tbl_1 values ('a ', 'a ');
Query OK, 1 row affected (0.01 sec)
mysql> select concat(one, '|') as one, concat(two, '|') as two from tbl_1;
+------+------+
| one | two |
+------+------+
| abc| | NULL |
| NULL | abc| |
| a| | a | |
+------+------+
3 rows in set (0.01 sec)
我们会发现在写入数据时char类型的字段不会保留空格。
1.2TEXT 和 BLOB
TEXT 和 BLOB都是保存纯文本数据类型,区别是TEXT保存字符数据, BLOB 保存二进制数据,并且BLOG有个特点,就是可以将图片转换成二进制保存到数据库中,但通常不建议这么使用,会影响数据库性能,在使用临时表查询结果中,如果有TEXT 和 BLOB会使服务器导致磁盘上的表而不是内存中的表,因为内存存储引擎不支持这些数据类型,这会导致检索速度非常慢。
1.3ENUM 和 SET
枚举数据类型。
1. 写入字段是创建表时所枚举出来的值
mysql> create table tbl_2 (name varchar(10), gender enum('男', '女'));
Query OK, 0 rows affected (0.08 sec)
mysql> insert into tbl_2 select 'a', '男';
Query OK, 1 row affected (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0
mysql> insert into tbl_2 select 'b', '女';
Query OK, 1 row affected (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0
mysql> insert into tbl_2 select 'c', '其他';
ERROR 1265 (01000): Data truncated for column 'gender' at row 1
ENUM 类型每个枚举值都有一个索引号,比如第一个指”男“的索引号是1,第二个值的索引号是2,空值索引值为0,通过索引号来查询:
mysql> select * from tbl_2 where gender = 1;
+------+--------+
| name | gender |
+------+--------+
| a | 男 |
+------+--------+
1 row in set (0.01 sec)
由于索引号的存在,官方也不建议适应数字所谓枚举值,因为这样可能会混淆枚举值和索引号。
2. SET 类型允许将零个或多个枚举值放在一起:
mysql> create table tbl_3(one set('a', 'b', 'c', 'd'));
Query OK, 0 rows affected (0.03 sec)
mysql> insert into tbl_3 values('a,b,c'), ('a,a'), ('b,d,b');
Query OK, 3 rows affected (0.00 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> select * from tbl_3;
+-------+
| one |
+-------+
| a,b,c |
| a |
| b,d |
+-------+
3 rows in set (0.00 sec)
3. 写入枚举值之外的值
mysql> insert into tbl_3 values ('a,b,e');
ERROR 1265 (01000): Data truncated for column 'one' at row 1
1.4如何选择
以下是您提供的数据库字段类型及应用场景转换为Markdown格式的表格:
| 类型 | 应用场景 |
|---|---|
| CHAR | 固定长度的字符串 |
| VARCHAR | 可变长度的字符串 |
| TEXT | 长文本(通常不建议使用,会影响查询效率) |
| BLOG | 二进制形式的长文本 (通常不建议使用,会影响查询效率) |
| ENUM | 枚举场景,并且只使用一个枚举值 |
| SET | 枚举场景,使用多个枚举值(使用较少) |
2整数类型
2.1INT
用来存放整数类型,这是最常见的类型,比如数量等。在开发中也常见将主键设置为INT类型。
通过实例验证:
1. 创建库表
mysql> create database test2;
Query OK, 1 row affected (0.03 sec)
mysql> use test2
Database changed
mysql> create table tbl_1(id int);
Query OK, 0 rows affected (0.03 sec)
2.插入默认有符号最大值
mysql> insert into tbl_1 select 2147483647;
Query OK, 1 row affected (0.03 sec)
Records: 1 Duplicates: 0 Warnings: 0
3. 最大值+1
mysql> insert into tbl_1 select 2147483648;
ERROR 1264 (22003): Out of range value for column 'id' at row 1
mysql>
4. 创建无符号INT类型
mysql> create table tbl_2(id int unsigned);
Query OK, 0 rows affected (0.03 sec)
mysql> insert into tbl_2 select 2147483648;
Query OK, 1 row affected (0.01 sec)
Records: 1 Duplicates: 0 Warnings: 0
此时默认有符号最大值加1后的值就可以正常插入
5. 无符号整型支持的最大值是 4294967295
mysql> insert into tbl_2 select 4294967295;
Query OK, 1 row affected (0.03 sec)
Records: 1 Duplicates: 0 Warnings: 0
mysql> insert into tbl_2 select 4294967296;
ERROR 1264 (22003): Out of range value for column 'id' at row 1
2.2TINYINT
特点:占用空间少,仅有1字节,但存放数据也有限,无符号数字只能存储0255,有符号可以存储-128127。
2.3SMALLINT
比TINYINT存放的数据多一点,范围是0~65525。
2.4MEDIUMINT
占用空间为3字节。
2.5BIGINT
占用空间8字节。
2.6如何选择
| 类型 | 大小 (Bytes) | 有符号范围 | 无符号范围 |
|---|---|---|---|
| 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]$ |
3浮点类型
3.1FLOAT 单精度浮点数
表示近似的数据值。
通过实例验证:
1. 创建库表
create database test3;
use test3
create table tbl_1(a float);
2. 插入记录
insert into tbl_1 select 3.14;
mysql> select * from tbl_1;
+------+
| a |
+------+
| 3.14 |
+------+
3.2DOUBLE 双精度浮点数
- 占用8字节
3.3DECIMAL 定点类型
- 用于存储精确的数据,比如金额数量。
通过实例验证:
1. 创建库表
create database test4;
use test4
create table tbl_1(a decimal(5, 2));
decimal(5, 2) 表示精度为5,刻度为2,可以用5位有效数字,2位小数。
2. 插入记录
insert into tbl_1 select -999.99;
insert into tbl_1 select 999.99;
select * from tbl_1;
+------+
| a |
+------+
| 3.14 |
+------+
mysql> insert into tbl_1 select -10000.99;
ERROR 1264 (22003): Out of range value for column 'a' at row 1
3. 写入超过刻度的数据
mysql> insert into tbl_1 select 3.141;
Query OK, 1 row affected, 1 warning (0.01 sec)
Records: 1 Duplicates: 0 Warnings: 1
mysql> select * from tbl_1;
+---------+
| a |
+---------+
| -999.99 |
| 999.99 |
| 3.14 |
+---------+
3 rows in set (0.00 sec)
会发现超过刻度的数据只保留两2位小数。
4. 浮点数与定点数区别
mysql> create table tbl_2(a float, b decimal(8, 4));
Query OK, 0 rows affected (0.03 sec)
mysql> insert into tbl_2 select 7777.7777, 7777.7777;
Query OK, 1 row affected (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0
mysql> insert into tbl_2 select 66.6666, 66.6666;
Query OK, 1 row affected (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0
mysql> select * from tbl_2;
+---------+-----------+
| a | b |
+---------+-----------+
| 7777.78 | 7777.7777 |
| 66.6666 | 66.6666 |
+---------+-----------+
2 rows in set (0.01 sec)
从实验可以看到float的数据存储到表中会存在误差,使用中禁止使用float类型来保存于金钱相关的数据。
5. 默认定点类型精度
mysql> create table tbl_3(a decimal);
Query OK, 0 rows affected (0.13 sec)
mysql> show create table tbl_3;
+-------+----------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-------+----------------------------------------------------------------------------------------------------------------------------+
| tbl_3 | CREATE TABLE `tbl_3` (
`a` decimal(10,0) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci |
+-------+----------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
发现它会默认转换为 decimal(10,0)。
3.4场景选择
| 类型 | 大小 (Bytes) | 有符号范围 | 无符号范围 |
|---|---|---|---|
| FLOAT | $4$ | $(-3.402823466E+38, -1.175494351E-38), 0, (1.175494351E-38, 3.402823466351E+38)$ | $0, (1.175494351E-38, 3.402823466E+38)$ |
| DOUBLE | $8$ | $(-1.7976931348623157E+308, -2.2250738585072014E-308), 0, (2.2250738585072014E-308, 1.7976931348623157E+308)$ | $0, (2.2250738585072014E-308, 1.7976931348623157E+308)$ |
| DECIMAL | 精度 $+ 2$ | 依赖于精度(Precision)和标度(Scale) | 依赖于精度(Precision)和标度(Scale) |
补充说明:
- FLOAT 和 DOUBLE:属于近似值类型(浮点数),在涉及财务或非误差允许的计算(如金额)时,不建议使用,因为会存在精度损失。
- DECIMAL:属于精确值类型(定点数)。它以字符串形式存储,通常用于银行账目等对精度要求极高的场景。
- 精度 (M):总位数(不包括小数点)。
- 标度 (D):小数点后的位数。
- 例如
DECIMAL(10,2)表示总共 10 位,其中 2 位是小数。
4时间类型
4.1DATE
- 日期类型。存储的年月日
1. 创建库表
create database test5;
use test5
create table tbl_1(a date);
2. 插入记录
insert into tbl_1 select '2026-05-21';
mysql> select * from tbl_1;
+------------+
| a |
+------------+
| 2026-05-21 |
+------------+
1 row in set (0.01 sec)
4.2TIME
- 时间类型,存储的时分秒
插入记录
create table tbl_2(a time);
insert into tbl_2 select '20:08:08';
mysql> select * from tbl_2;
+----------+
| a |
+----------+
| 20:08:08 |
+----------+
1 row in set (0.00 sec)
插入超过范围的记录
mysql> insert into tbl_2 select '838:08:08';
Query OK, 1 row affected (0.02 sec)
Records: 1 Duplicates: 0 Warnings: 0
mysql> insert into tbl_2 select '839:08:08';
ERROR 1292 (22007): Incorrect time value: '839:08:08' for column 'a' at row 1
4.3YEAR
- 存储4位格式的年份
mysql> create table tbl_3(a year);
Query OK, 0 rows affected (0.04 sec)
mysql> insert into tbl_3 select '2026';
Query OK, 1 row affected (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0
mysql> select * from tbl_3;
+------+
| a |
+------+
| 2026 |
+------+
1 row in set (0.00 sec)
插入超过范围的记录
mysql> insert into tbl_3 select '2156';
ERROR 1264 (22003): Out of range value for column 'a' at row 1
mysql>
4.4DATETIME
- 存储年月日时分秒的数据
4.5TIMESTAMP
- 存储年月日时分秒的数据,但是带有时区的属性,它会随服务器时区变化。
create table tbl_4(a datetime, b timestamp);
insert into tbl_4 values(now(), now());
select * from tbl_4;
+---------------------+---------------------+
| a | b |
+---------------------+---------------------+
| 2026-05-21 12:39:53 | 2026-05-21 12:39:53 |
+---------------------+---------------------+
1 row in set (0.00 sec)
可以看到写入的时间是一样的。
现在调整数据库时区,然后查询数据:
mysql> set time_zone='+05:00';
Query OK, 0 rows affected (0.00 sec)
mysql> select * from tbl_4;
+---------------------+---------------------+
| a | b |
+---------------------+---------------------+
| 2026-05-21 12:39:53 | 2026-05-21 09:39:53 |
+---------------------+---------------------+
1 row in set (0.00 sec)
会发现两个字段的值变了,这就表示了timestamp会随着时区而变化。
记录数据更新时间和创建时间:
create table tbl_5(
id int primary key auto_increment comment '主键',
name varchar(10),
create_time timestamp not null default CURRENT_TIMESTAMP comment '创建时间',
update_time timestamp not null default CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP comment '更新时间'
) engine=innodb charset=utf8mb4;
insert into tbl_5(name) select 'aa';
mysql> select * from tbl_5;
+----+------+---------------------+---------------------+
| id | name | create_time | update_time |
+----+------+---------------------+---------------------+
| 1 | aa | 2026-05-21 12:47:55 | 2026-05-21 12:47:55 |
+----+------+---------------------+---------------------+
1 row in set (0.00 sec)
mysql> update tbl_5 set name='bb' where id = 1;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select * from tbl_5;
+----+------+---------------------+---------------------+
| id | name | create_time | update_time |
+----+------+---------------------+---------------------+
| 1 | bb | 2026-05-21 12:47:55 | 2026-05-21 12:49:30 |
+----+------+-----------
- CURRENT_TIMESTAMP 表示新增数据时如果不指定时间则默认使用当前时间。
- ON UPDATE CURRENT_TIMESTAMP 表示如果记录出现更新,则修改更新时间为当前时间。
4.6选择
根据您提供的第三张图片,关于日期和时间类型的 Markdown 表格如下:
| 类型 | 大小 (Bytes) | 范围 | 格式 | 用途 |
|---|---|---|---|---|
| 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’ UTC 到 ‘2038-01-19 03:14:07’ | YYYY-MM-DD hh:mm:ss | 混合日期和时间值 |
核心区别:
- DATETIME vs TIMESTAMP:
- 范围:
DATETIME范围更广(公元 1000 年开始),而TIMESTAMP只能支持到 2038 年(受限于 $32$ 位存储)。 - 时区:
TIMESTAMP与时区相关,会根据数据库服务器的时区设置进行转换,而DATETIME存储后是什么样,读出来就是什么样,不受时区影响。 - 空间:
TIMESTAMP仅占 $4$ 字节,比DATETIME的 $8$ 字节更省空间。
- 范围:
- TIME:它的范围其实超过了 24 小时($-838$ 到 $838$),因为它不仅可以表示一天内的时间,还可以用来记录两个事件之间的时间间隔(持续时间)。
更新日志
- 2026-05-21 重新执行所有操作语句。