【MySQL】数据类型

文章目录

    • [1. 数据类型分类](#1. 数据类型分类)
    • [2. 数值类型](#2. 数值类型)
      • [2.1 tinyint 类型](#2.1 tinyint 类型)
      • [2.2 bit 类型](#2.2 bit 类型)
      • [2.3 小数类型](#2.3 小数类型)
        • [2.3.1 float](#2.3.1 float)
        • [2.3.2 decimal](#2.3.2 decimal)
    • [3. 字符串类型](#3. 字符串类型)
      • [3.1 char](#3.1 char)
      • [3.2 varchar](#3.2 varchar)
      • [3.3 char和varchar比较](#3.3 char和varchar比较)
    • [4. 日期和时间类型](#4. 日期和时间类型)
    • [5. enum和set](#5. enum和set)
      • [5.1 集合查询使用 find_ in_ set 函数:](#5.1 集合查询使用 find_ in_ set 函数:)

1. 数据类型分类

下图展示的是数据库中常见的数据类型分类及说明:

2. 数值类型

下面是关于数据库中整数类型(TINYINT、SMALLINT、MEDIUMINT、INT、BIGINT)的字节数、最小值和最大值(带符号 / 无符号)的表格:

2.1 tinyint 类型

在测试之前,我们还是先建一个库:

sql 复制代码
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| RECOVER_YOUR_DATA  |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
5 rows in set (0.00 sec)

mysql> create database test_db;
Query OK, 1 row affected (0.00 sec)

mysql> use test_db;
Database changed

然后再建一张表:

sql 复制代码
mysql> create table if not exists t1(
    -> num tinyint
    -> );
Query OK, 0 rows affected (0.02 sec)

接着,我们有三种方式可以查看表结构:

现在进行数值越界测试,先插入范围内的值:

sql 复制代码
mysql> insert into t1 values (-128);
Query OK, 1 row affected (0.01 sec)

mysql> insert into t1 values (127);
Query OK, 1 row affected (0.00 sec)

mysql> insert into t1 values (0);
Query OK, 1 row affected (0.00 sec)

mysql> insert into t1 values (-1);
Query OK, 1 row affected (0.01 sec)

mysql> insert into t1 values (1);
Query OK, 1 row affected (0.00 sec)

mysql> select * from t1;
+------+
| num  |
+------+
| -128 |
|  127 |
|    0 |
|   -1 |
|    1 |
+------+
5 rows in set (0.01 sec)

结果如下:

再插入范围之外的值:

sql 复制代码
mysql> insert into t1 values (-129);
ERROR 1264 (22003): Out of range value for column 'num' at row 1

mysql> insert into t1 values (128);
ERROR 1264 (22003): Out of range value for column 'num' at row 1

mysql> insert into t1 values (130);
ERROR 1264 (22003): Out of range value for column 'num' at row 1

结果如下:

说明:

  • 在 MySQL 中,整型可以指定是有符号的和无符号的,默认是有符号的。
  • 可以通过 UNSIGNED 来说明某个字段是无符号的。

无符号案例如下(先建一张无符号的表):

sql 复制代码
mysql> create table if not exists t2(
    -> num tinyint unsigned
    -> );
Query OK, 0 rows affected (0.02 sec)

mysql> desc t2;
+-------+---------------------+------+-----+---------+-------+
| Field | Type                | Null | Key | Default | Extra |
+-------+---------------------+------+-----+---------+-------+
| num   | tinyint(3) unsigned | YES  |     | NULL    |       |
+-------+---------------------+------+-----+---------+-------+
1 row in set (0.01 sec)

mysql> insert into t2 values(0);
Query OK, 1 row affected (0.00 sec)

mysql> insert into t2 values(255);
Query OK, 1 row affected (0.00 sec)

mysql> insert into t2 values(100);
Query OK, 1 row affected (0.00 sec)

mysql> insert into t2 values(-1);
ERROR 1264 (22003): Out of range value for column 'num' at row 1

结果如下:

说明:

  • 如果我们向 MySQL 特定的类型中插入不合法的数据,那么 MySQL 一般都是直接拦截我们的,不让我们做对应的操作。
  • 相反,如果我们已经有数据被成功插入到 MySQL 中了,那么一定插入的时候是合法的。
  • 所以,在 MySQL 中,一般而言,数据类型本身也是一种 "约束"。这种 "约束" 是让 "使用者" 尽可能的进行正确的插入。

注意:

  • 尽量不使用 unsigned,对于 int 类型可能存放不下的数据,int unsigned 同样可能存放不下,与其如此,还不如设计时,将 int 类型提升为 bigint 类型。

2.2 bit 类型

基本语法:

sql 复制代码
bit[(M)] : 位字段类型。M表示每个值的位数,范围从1到64。如果M被忽略,默认为1。

我们还是先建表

sql 复制代码
mysql> create table if not exists t3(
    -> id int,
    -> online bit(1)
    -> );
Query OK, 0 rows affected (0.01 sec)

mysql> desc t3;
+--------+---------+------+-----+---------+-------+
| Field  | Type    | Null | Key | Default | Extra |
+--------+---------+------+-----+---------+-------+
| id     | int(11) | YES  |     | NULL    |       |
| online | bit(1)  | YES  |     | NULL    |       |
+--------+---------+------+-----+---------+-------+
2 rows in set (0.00 sec)

然后再插入数据:

sql 复制代码
mysql> insert into t3 (id, online) values (123, 0);
Query OK, 1 row affected (0.00 sec)

mysql> insert into t3 (id, online) values (124, 1);
Query OK, 1 row affected (0.00 sec)

mysql> insert into t3 (id, online) values (124, 3);
ERROR 1406 (22001): Data too long for column 'online' at row 1

mysql> insert into t3 (id, online) values (124, 5);
ERROR 1406 (22001): Data too long for column 'online' at row 1

mysql> insert into t3 (id, online) values (124, 2);
ERROR 1406 (22001): Data too long for column 'online' at row 1

因为我们的 online 设置的是 1 个 bit 位,所以只能是 0 / 1,其余数字都不行。

另外,我们查表可以看到 id 能正常显示,online 却无法显示出来:

因为 online 的类型是比特位嘛,它在这里显示时,其实是通常会给我们按照 ASCII 码值的形式显示的。只不过当前 online 这里对应的 ASCII 码值是不可显示的。

我们可以用十进制的方式来显示 bit 位:

我们知道,bit 字段在显示时,是按照 ASCII 码对应的值显示的。但是如何验证呢?

我们先更改 t3 表中 online 的类型:

sql 复制代码
mysql> alter table t3 modify online bit(10);
Query OK, 2 rows affected (0.04 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> desc t3;
+--------+---------+------+-----+---------+-------+
| Field  | Type    | Null | Key | Default | Extra |
+--------+---------+------+-----+---------+-------+
| id     | int(11) | YES  |     | NULL    |       |
| online | bit(10) | YES  |     | NULL    |       |
+--------+---------+------+-----+---------+-------+
2 rows in set (0.00 sec)

然后再插入数据:

sql 复制代码
mysql> insert into t3 (id, online) values (125, 6);
Query OK, 1 row affected (0.00 sec)

mysql> insert into t3 (id, online) values (127, 5);
Query OK, 1 row affected (0.00 sec)

mysql> insert into t3 (id, online) values (128, 'a');
Query OK, 1 row affected (0.00 sec)

mysql> insert into t3 (id, online) values (129, 97);
Query OK, 1 row affected (0.00 sec)

结果如下,可以看到,我们插入 'a' 和 97 的效果是一样的:

另外,如果不指定 bit 的范围,那么默认就是 bit(1):

如果我们有这样的值,只存放 0 或 1,这时可以定义 bit(1),这样可以节省空间。

2.3 小数类型

2.3.1 float

语法:

sql 复制代码
float[(m, d)] [unsigned] : M指定显示长度,d指定小数位数,占用空间4个字节

还是先建立一张表:

sql 复制代码
mysql> create table if not exists t5 (
    -> id int,
    -> salary float(4,2)
    -> );
Query OK, 0 rows affected (0.01 sec)

mysql> desc t5;
+--------+------------+------+-----+---------+-------+
| Field  | Type       | Null | Key | Default | Extra |
+--------+------------+------+-----+---------+-------+
| id     | int(11)    | YES  |     | NULL    |       |
| salary | float(4,2) | YES  |     | NULL    |       |
+--------+------------+------+-----+---------+-------+
2 rows in set (0.00 sec)

案例一:float(4,2) 表示的范围是 -99.99 ~ 99.99,MySQL 在保存值时会进行 四舍五入

sql 复制代码
mysql> insert into t5 (id, salary) values (1, 99.99);
Query OK, 1 row affected (0.00 sec)

mysql> insert into t5 (id, salary) values (1, -99.99);
Query OK, 1 row affected (0.00 sec)

mysql> insert into t5 (id, salary) values (2, -99.991);
Query OK, 1 row affected (0.00 sec)

mysql> insert into t5 (id, salary) values (2, -99.996);
ERROR 1264 (22003): Out of range value for column 'salary' at row 1

mysql> insert into t5 (id, salary) values (2, -99.995);
ERROR 1264 (22003): Out of range value for column 'salary' at row 1

mysql> insert into t5 (id, salary) values (3, -99.994);
Query OK, 1 row affected (0.00 sec)

结果如下:

那么思考一下: 当我们的 float(4,2) 如果是一个有符号的,则表示范围是 -99.99 ~ 99.99,如果 float(6,3),请问范围是多少?

答案是:-999.999 ~ 999.999

案例二:如果定义的是 float(4,2) unsigned 这时,因为把它指定为无符号的数,范围是 0 ~ 99.99

还是先建表:

sql 复制代码
mysql> create table if not exists t6 (
    -> id bigint,
    -> salary float(4,2) unsigned
    -> );
Query OK, 0 rows affected (0.01 sec)

mysql> desc t6;
+--------+---------------------+------+-----+---------+-------+
| Field  | Type                | Null | Key | Default | Extra |
+--------+---------------------+------+-----+---------+-------+
| id     | bigint(20)          | YES  |     | NULL    |       |
| salary | float(4,2) unsigned | YES  |     | NULL    |       |
+--------+---------------------+------+-----+---------+-------+
2 rows in set (0.00 sec)

然后再插入数据

sql 复制代码
mysql> insert into t6 (id, salary) values (1,0);
Query OK, 1 row affected (0.00 sec)

mysql> insert into t6 (id, salary) values (2,99.99);
Query OK, 1 row affected (0.01 sec)

mysql> insert into t6 (id, salary) values (3,99.994);
Query OK, 1 row affected (0.00 sec)

mysql> insert into t6 (id, salary) values (4,99.995);
ERROR 1264 (22003): Out of range value for column 'salary' at row 1

mysql> insert into t6 (id, salary) values (4,-99.99);
ERROR 1264 (22003): Out of range value for column 'salary' at row 1

mysql> insert into t6 (id, salary) values (4,34.567);
Query OK, 1 row affected (0.01 sec)

mysql> select * from t6;
+------+--------+
| id   | salary |
+------+--------+
|    1 |   0.00 |
|    2 |  99.99 |
|    3 |  99.99 |
|    4 |  34.57 |
+------+--------+
4 rows in set (0.00 sec)

结果如下:

2.3.2 decimal

语法:

sql 复制代码
decimal(m, d) [unsigned] : 定点数m指定长度,d表示小数点的位数

说明:

  • decimal(5,2) 表示的范围是 -999.99 ~ 999.99
  • decimal(5,2) unsigned 表示的范围 0 ~ 999.99
  • decimal 和 float 很像,但是有区别,它们表示的精度不一样

还是先建立一张表:

sql 复制代码
mysql> create table if not exists t7 (
    -> id int,
    -> f1 float(10, 8),
    -> f2 decimal(10, 8)
    -> );
Query OK, 0 rows affected (0.01 sec)

mysql> desc t7;
+-------+---------------+------+-----+---------+-------+
| Field | Type          | Null | Key | Default | Extra |
+-------+---------------+------+-----+---------+-------+
| id    | int(11)       | YES  |     | NULL    |       |
| f1    | float(10,8)   | YES  |     | NULL    |       |
| f2    | decimal(10,8) | YES  |     | NULL    |       |
+-------+---------------+------+-----+---------+-------+
3 rows in set (0.00 sec)

案例如下:

sql 复制代码
mysql> insert into t7 (id, f1, f2) values (111, 23.12345612, 23.12345612);
Query OK, 1 row affected (0.00 sec)

mysql> select * from t7;
+------+-------------+-------------+
| id   | f1          | f2          |
+------+-------------+-------------+
|  111 | 23.12345695 | 23.12345612 |
+------+-------------+-------------+
1 row in set (0.00 sec)

可以发现 decimal 的精度更准确,因此如果我们希望某个数据表示高精度,选择 decimal。

说明:

  • float 表示的精度大约是 7 位。
  • decimal 整数最大位数 m 为 65。支持小数最大位数 d 是 30。如果 d 被省略,默认为 0,如果 m 被省略,默认是 10。

3. 字符串类型

3.1 char

语法:

sql 复制代码
char(L): 固定长度字符串,L是可以存储的长度,单位为字符,最大长度值可以为255

先建表:

sql 复制代码
mysql> create table if not exists t8 (
    -> id int,
    -> name char(2)
    -> );
Query OK, 0 rows affected (0.02 sec)

mysql> desc t8;
+-------+---------+------+-----+---------+-------+
| Field | Type    | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id    | int(11) | YES  |     | NULL    |       |
| name  | char(2) | YES  |     | NULL    |       |
+-------+---------+------+-----+---------+-------+
2 rows in set (0.00 sec)

案例:

sql 复制代码
mysql> insert into t8 (id, name) values (100, 'a');
Query OK, 1 row affected (0.00 sec)

mysql> insert into t8 (id, name) values (101, 'ab');
Query OK, 1 row affected (0.00 sec)

mysql> insert into t8 (id, name) values (102, 'abc');
ERROR 1406 (22001): Data too long for column 'name' at row 1

mysql> insert into t8 (id, name) values (103, '你');
Query OK, 1 row affected (0.00 sec)

mysql> insert into t8 (id, name) values (104, '你好');
Query OK, 1 row affected (0.00 sec)

mysql> insert into t8 (id, name) values (105, '你好吗');
ERROR 1406 (22001): Data too long for column 'name' at row 1
mysql> 
mysql> select * from t8;
+------+--------+
| id   | name   |
+------+--------+
|  100 | a      |
|  101 | ab     |
|  103 | 你     |
|  104 | 你好   |
+------+--------+
4 rows in set (0.00 sec)

结果如下:

说明:char(2) 表示可以存放两个字符,可以是字母或汉字,但是不能超过 2 个, 最多只能是 255。

3.2 varchar

语法:

sql 复制代码
varchar(L): 可变长度字符串,L表示字符长度,最大长度65535个字节

案例:

sql 复制代码
mysql> create table if not exists t9 (
    -> id int,
    -> name varchar(6)
    -> );
Query OK, 0 rows affected (0.02 sec)

mysql> insert into t9 (id, name) values (111, 'hello');
Query OK, 1 row affected (0.00 sec)

mysql> insert into t9 (id, name) values (222, '我爱你,世界');
Query OK, 1 row affected (0.00 sec)

mysql> insert into t9 (id, name) values (333, '我爱你,大帅哥');
ERROR 1406 (22001): Data too long for column 'name' at row 1

mysql> select * from t9;
+------+--------------------+
| id   | name               |
+------+--------------------+
|  111 | hello              |
|  222 | 我爱你,世界       |
+------+--------------------+
2 rows in set (0.00 sec)

结果如下:

关于 varchar(len) 中的 len 到底是多大,这个 len 值,和表的编码密切相关:

  • varchar 的字节长度可以指定为 0 到 65535 之间的值,但是有 1 - 3 个字节用于记录数据大小,所以说有效字节数是65532。
  • 当我们的表的编码是 utf8 时,varchar(n) 的参数 n 最大值是 65532/3=21844(因为 utf 中,一个字符占用 3 个字节),如果编码是 gbk,varchar(n) 的参数 n 最大是 65532/2=32766(因为 gbk 中,一个字符占用 2 字节)。

我们现在可以验证一下 utf8 确实是不能超过 21844:

3.3 char和varchar比较

如下表所示:

如何选择定长或变长字符串?

  • 如果数据确定长度都一样,就使用定长(char),比如:身份证,手机号,md5。
  • 如果数据长度有变化,就使用变长(varchar),比如:名字,地址,但是你要保证最长的能存的进去。
  • 定长的磁盘空间比较浪费,但是效率高。
  • 变长的磁盘空间比较节省,但是效率低。
  • 定长的意义是,直接开辟好对应的空间。
  • 变长的意义是,在不超过自定义范围的情况下,用多少,开辟多少。

4. 日期和时间类型

常用的日期有如下三个:

  • date:日期 'yyyy-mm-dd',占用 3 字节。
  • datetime 时间日期格式 'yyyy-mm-dd HH:ii:ss' 表示范围从 1000 到 9999,占用 8 字节。
  • timestamp:时间戳,从 1970 年开始的 yyyy-mm-dd HH:ii:ss 格式和 datetime 完全一致,占用 4 字节。

先创建表:

sql 复制代码
mysql> create table if not exists t11 (
    -> t1 date,
    -> t2 datetime,
    -> t3 timestamp
    -> );
Query OK, 0 rows affected (0.02 sec)

mysql> desc t11;
+-------+-----------+------+-----+-------------------+-----------------------------+
| Field | Type      | Null | Key | Default           | Extra                       |
+-------+-----------+------+-----+-------------------+-----------------------------+
| t1    | date      | YES  |     | NULL              |                             |
| t2    | datetime  | YES  |     | NULL              |                             |
| t3    | timestamp | NO   |     | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
+-------+-----------+------+-----+-------------------+-----------------------------+
3 rows in set (0.00 sec)

再插入数据:

sql 复制代码
mysql> insert into t11 (t1, t2) values ('2008-10-01', '2010-10-01 8:30:05');
Query OK, 1 row affected (0.00 sec)

mysql> select * from t11;
+------------+---------------------+---------------------+
| t1         | t2                  | t3                  |
+------------+---------------------+---------------------+
| 2008-10-01 | 2010-10-01 08:30:05 | 2025-10-16 12:47:53 |
+------------+---------------------+---------------------+
1 row in set (0.00 sec)

添加数据时,时间戳自动补上当前时间,结果如下:

那么我们现在可以更新 t1 的数据,可以看到更新数据,时间戳会更新成当前时间:

5. enum和set

enum:枚举,"单选" 类型;

sql 复制代码
enum('选项1','选项2','选项3',...); 

说明:

  • 该设定只是提供了若干个选项的值,最终一个单元格中,实际只存储了其中一个值;
  • 而且出于效率考虑,这些值实际存储的是 "数字",因为这些选项的每个选项值依次对应如下数字:1,2,3,....,最多65535个;
  • 当我们添加枚举值时,也可以添加对应的数字编号。

set:集合,"多选"类型;

sql 复制代码
set('选项值1','选项值2','选项值3', ...);

说明:

  • 该设定只是提供了若干个选项的值,最终一个单元格中,设计可存储了其中任意多个值;
  • 而且出于效率考虑,这些值实际存储的是 "数字",因为这些选项的每个选项值依次对应如下数字:1,2,4,8,16,32,....,最多64个。

另外需要注意:不建议在添加枚举值,集合值的时候采用数字的方式,因为不利于阅读。

案例:有一个调查表 votes,需要调查人的喜好, 比如(登山,游泳,篮球,武术)中去选择(可以多选),其中性别 男 / 女 是单选。

sql 复制代码
mysql> create table if not exists votes (
    -> username varchar(30),
    -> gender enum('男', '女'),
    -> hobby set('登山', '游泳', '篮球', '唱歌', '写代码')
    -> );
Query OK, 0 rows affected (0.01 sec)

mysql> desc votes;
+----------+------------------------------------------------------+------+-----+---------+-------+
| Field    | Type                                                 | Null | Key | Default | Extra |
+----------+------------------------------------------------------+------+-----+---------+-------+
| username | varchar(30)                                          | YES  |     | NULL    |       |
| gender   | enum('男','女')                                      | YES  |     | NULL    |       |
| hobby    | set('登山','游泳','篮球','唱歌','写代码')            | YES  |     | NULL    |       |
+----------+------------------------------------------------------+------+-----+---------+-------+
3 rows in set (0.00 sec)

插入数据:

sql 复制代码
mysql> insert into votes values ('张三', '男', '写代码');
Query OK, 1 row affected (0.00 sec)

mysql> insert into votes values ('李四', '女', '唱歌,篮球');
Query OK, 1 row affected (0.00 sec)

mysql> select * from votes;
+----------+--------+---------------+
| username | gender | hobby         |
+----------+--------+---------------+
| 张三     | 男     | 写代码        |
| 李四     | 女     | 篮球,唱歌     |
+----------+--------+---------------+
2 rows in set (0.00 sec)

另外对于 enum 枚举类型,我们可以使用下标的方式进行插入(下标从 1 开始)

sql 复制代码
mysql> insert into votes values ('王五', 1, '登山,游泳');
Query OK, 1 row affected (0.00 sec)

mysql> insert into votes values ('赵六', 2, '登山,游泳');
Query OK, 1 row affected (0.00 sec)

mysql> select * from votes;
+----------+--------+---------------+
| username | gender | hobby         |
+----------+--------+---------------+
| 张三     | 男     | 写代码        |
| 李四     | 女     | 篮球,唱歌     |
| 王五     | 男     | 登山,游泳     |
| 赵六     | 女     | 登山,游泳     |
+----------+--------+---------------+
4 rows in set (0.00 sec)

结果如下:

同样,我们使用数字标识每个 爱好 的时候,能不能用这种方法呢?

其实这里就和 enum 不一样了,那么想想 Linux 权限,采用比特位的位置来和 set 中的 爱好 对应起来,也就是比特位的位置就代表 爱好

那么同时插入 5 个爱好,其实就是 2 5 − 1 2^5-1 25−1,也就是 31:

使用如下查询语句,但是该语句不能查询出所有,爱好为登山的人。

sql 复制代码
mysql> select * from votes where hobby='登山';
+----------+--------+--------+
| username | gender | hobby  |
+----------+--------+--------+
| 胡九     | 男     | 登山   |
+----------+--------+--------+
1 row in set (0.00 sec)

结果如下:

5.1 集合查询使用 find_ in_ set 函数:

find_in_set(sub,str_list) :如果 sub 在 str_list 中,则返回下标;如果不在,返回 0;

其中 str_list 用逗号分隔的字符串。

可以看到字符 a 在字符串 a, b, c 中,故返回结果为 1;同理,如果不在,返回结果为 0。

那如果我要查询爱好为 登山 的所有人呢?

sql 复制代码
mysql> select * from votes where find_in_set('登山', hobby);
+----------+--------+---------------------------------------+
| username | gender | hobby                                 |
+----------+--------+---------------------------------------+
| 王五     | 男     | 登山,游泳                             |
| 赵六     | 女     | 登山,游泳                             |
| 田七     | 男     | 登山,游泳,写代码                      |
| 胡九     | 男     | 登山                                  |
| tom      | 男     | 登山                                  |
| andy     | 男     | 登山,游泳                             |
| Rolly    | 男     | 登山,游泳,篮球,唱歌,写代码            |
+----------+--------+---------------------------------------+
7 rows in set (0.00 sec)

结果如下:

还可以查询爱好中既有登山,又有游泳的用户:

相关推荐
遇见火星13 小时前
MYSQL-物理备份(xtrabackup)使用指南
数据库·mysql·adb
爱可生开源社区13 小时前
医疗业务系统升级,这家三甲医院为何牵手 OceanBase?(SQLServer->OceanBase)
数据库
huihuihuanhuan.xin13 小时前
后端八股之mysql
数据库·mysql
洋不写bug14 小时前
数据库数据类型,数据值类型,字符串类型,日期类型详解
数据库·mysql
Paraverse_徐志斌14 小时前
RAG架构(检索增强生成)与向量数据库
数据库·ai·llm·embedding·milvus·rag
NineData15 小时前
NineData将亮相第27届GOPS全球运维大会,并带来技术演讲
运维·数据库·ninedata·智能·ai agent·数据管理工具·gops全球运维大会
Java水解15 小时前
MySQL 中 ROW_NUMBER() 函数详解
后端·mysql
不良人天码星15 小时前
谈谈redis的持久化
数据库·redis·缓存
huibin14785236916 小时前
不能识别adb/usb口记录
android·adb