【MySQL篇】数据类型:存储数据的基础

文章目录

    • 数据类型:存储数据的基础
    • 一、前言
    • 二、数据类型分类
      • [2.1 MySQL 数据类型概览](#2.1 MySQL 数据类型概览)
    • 三、数值类型
      • [3.1 整数类型](#3.1 整数类型)
      • [3.2 BIT 类型](#3.2 BIT 类型)
        • 语法
        • [BIT 的特点](#BIT 的特点)
        • [BIT 示例](#BIT 示例)
        • [BIT(1) 实例](#BIT(1) 实例)
        • [BIT 的使用建议](#BIT 的使用建议)
      • [3.3 浮点数类型](#3.3 浮点数类型)
    • 四、字符串类型
      • [4.1 CHAR 类型(定长字符串)](#4.1 CHAR 类型(定长字符串))
        • 语法
        • [CHAR 示例](#CHAR 示例)
        • [CHAR 的特点](#CHAR 的特点)
      • [4.2 VARCHAR 类型(变长字符串)](#4.2 VARCHAR 类型(变长字符串))
        • 语法
        • [VARCHAR 长度的计算](#VARCHAR 长度的计算)
        • [VARCHAR 示例](#VARCHAR 示例)
        • [VARCHAR 长度验证](#VARCHAR 长度验证)
      • [4.3 CHAR vs VARCHAR](#4.3 CHAR vs VARCHAR)
    • 五、日期时间类型
      • [5.1 DATE 类型](#5.1 DATE 类型)
      • [5.2 DATETIME 类型](#5.2 DATETIME 类型)
      • [5.3 TIMESTAMP 类型](#5.3 TIMESTAMP 类型)
      • [5.4 日期时间类型示例](#5.4 日期时间类型示例)
      • [5.5 日期时间类型选择建议](#5.5 日期时间类型选择建议)
    • 六、枚举和集合
      • [6.1 ENUM 类型(单选)](#6.1 ENUM 类型(单选))
        • 语法
        • [ENUM 示例](#ENUM 示例)
      • [6.2 SET 类型(多选)](#6.2 SET 类型(多选))
        • 语法
        • [SET 示例](#SET 示例)
      • [6.3 SET 的查询](#6.3 SET 的查询)
      • [6.4 ENUM 和 SET 的对比](#6.4 ENUM 和 SET 的对比)
      • [6.5 ENUM 和 SET 的使用建议](#6.5 ENUM 和 SET 的使用建议)
    • 七、其他字符串类型
      • [7.1 TEXT 类型](#7.1 TEXT 类型)
      • [7.2 BLOB 类型](#7.2 BLOB 类型)
    • 八、数据类型选择指南
      • [8.1 完整的数据类型对比表](#8.1 完整的数据类型对比表)
      • [8.2 常见表设计示例](#8.2 常见表设计示例)
      • [8.3 设计表时的常见错误](#8.3 设计表时的常见错误)
    • 九、总结与最佳实践

数据类型:存储数据的基础

一、前言

💬 这一篇讲什么:MySQL 中的各种数据类型

🚀 核心内容

  • 整数、浮点数、字符串、日期等类型有什么区别?
  • 如何选择合适的数据类型?
  • 数据类型如何影响存储空间和性能?

在前一篇中,我们学会了创建表。表由若干列组成,每列都需要指定数据类型。数据类型决定了列能存储什么样的数据,以及存储的效率。选择正确的数据类型是数据库设计的关键。


二、数据类型分类

2.1 MySQL 数据类型概览

MySQL 支持多种数据类型,按照功能分为以下几类:

分类 类型 用途
数值类型 TINYINT、SMALLINT、INT、BIGINT、FLOAT、DECIMAL 存储数字
字符串类型 CHAR、VARCHAR、TEXT、BLOB 存储文本和二进制数据
日期时间类型 DATE、TIME、DATETIME、TIMESTAMP 存储日期和时间
特殊类型 ENUM、SET、JSON 存储枚举值、集合、JSON 数据

选择正确的数据类型有以下好处:

  • 节省存储空间:合适的类型占用更少磁盘空间。
  • 提高查询性能:数据类型影响索引效率和查询速度。
  • 数据完整性:正确的类型限制可以防止无效数据。
  • 易于维护:类型明确使代码更容易理解。

三、数值类型

3.1 整数类型

语法
sql 复制代码
TINYINT | SMALLINT | INT | BIGINT [UNSIGNED] [ZEROFILL]

UNSIGNED:表示无符号,即不能为负数。
ZEROFILL:不足指定宽度时用 0 补齐(不常用)。

各整数类型对比
类型 存储空间 有符号范围 无符号范围 使用场景
TINYINT 1 字节 -128 ~ 127 0 ~ 255 状态标记、布尔值
SMALLINT 2 字节 -32768 ~ 32767 0 ~ 65535 不常用
MEDIUMINT 3 字节 -8388608 ~ 8388607 0 ~ 16777215 不常用
INT 4 字节 -2^31 ~ 2^31-1 0 ~ 2^32-1 通常的整数存储
BIGINT 8 字节 -2^63 ~ 2^63-1 0 ~ 2^64-1 大数值、ID、时间戳
TINYINT 示例

有符号 TINYINT

sql 复制代码
CREATE TABLE tt1 (num TINYINT);

-- 插入有效值
INSERT INTO tt1 VALUES (1);
INSERT INTO tt1 VALUES (-128);
INSERT INTO tt1 VALUES (127);

-- 尝试超出范围
INSERT INTO tt1 VALUES (128);  -- 错误:超出范围
ERROR 1264 (22003): Out of range value for column 'num' at row 1

无符号 TINYINT

sql 复制代码
CREATE TABLE tt2 (num TINYINT UNSIGNED);

-- 插入有效值
INSERT INTO tt2 VALUES (0);
INSERT INTO tt2 VALUES (255);

-- 尝试插入负数或超出范围
INSERT INTO tt2 VALUES (-1);  -- 错误:无符号不能为负
ERROR 1264 (22003): Out of range value for column 'num' at row 1

INSERT INTO tt2 VALUES (256);  -- 错误:超出范围
ERROR 1264 (22003): Out of range value for column 'num' at row 1
INT 示例
sql 复制代码
CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    user_id INT COMMENT '用户ID',
    score INT DEFAULT 0 COMMENT '得分'
);

INSERT INTO users (user_id, score) VALUES (1001, 95);
INSERT INTO users (user_id, score) VALUES (1002, 87);

SELECT * FROM users;
选择整数类型的建议
  • 布尔标记 :使用 TINYINT(1)BOOLEAN(实际上是 TINYINT(1) 的别名)。
  • 用户 ID、数据库 ID :使用 BIGINT,避免未来溢出。
  • 普通数字 :通常用 INT
  • 不要使用 UNSIGNED:MySQL 官方建议避免 UNSIGNED。当值溢出时,用更大的类型(如从 INT 升级到 BIGINT)比用 UNSIGNED 更安全。

3.2 BIT 类型

语法
sql 复制代码
BIT(M)

M 表示位数,范围从 1 到 64。默认为 1。

BIT 的特点
  • 存储位(二进制)数据,每位只能是 0 或 1。
  • BIT(1) 只能存储 0 或 1。
  • BIT(8) 可以存储 0 ~ 255(8 位二进制数)。
  • 显示时按 ASCII 码对应的字符显示。
BIT 示例
sql 复制代码
CREATE TABLE tt4 (id INT, a BIT(8));

-- 插入数值 10
INSERT INTO tt4 VALUES (10, 10);

-- 查询时,10 对应的 ASCII 字符显示
SELECT * FROM tt4;

输出:

bash 复制代码
+----+------+
| id | a    |
+----+------+
| 10 |      |  -- 10 对应 ASCII 是不可见字符
+----+------+

插入 65(对应字符 'A'):

sql 复制代码
INSERT INTO tt4 VALUES (20, 65);
SELECT * FROM tt4;

输出:

bash 复制代码
+----+------+
| id | a    |
+----+------+
| 10 |      |
| 20 | A    |  -- 65 对应 ASCII 字符 'A'
+----+------+
BIT(1) 实例

存储 0 或 1 的状态:

sql 复制代码
CREATE TABLE tt5 (gender BIT(1));

INSERT INTO tt5 VALUES (0);  -- 成功
INSERT INTO tt5 VALUES (1);  -- 成功
INSERT INTO tt5 VALUES (2);  -- 错误:越界
ERROR 1406 (22001): Data too long for column 'gender' at row 1
BIT 的使用建议
  • 场景有限 :BIT 类型使用场景不多。通常用 TINYINT(1) 代替 BIT(1) 更清晰。
  • 不建议使用:显示困扰(按 ASCII 码显示)且用途有限。
  • 如果必须用:BIT(1) 存储 true/false,BIT(8) 存储权限位掩码等。

3.3 浮点数类型

FLOAT 类型

语法

sql 复制代码
FLOAT(M, D) [UNSIGNED]
  • M:指定显示长度(包括整数部分和小数部分)。
  • D:指定小数位数。
  • 存储空间:4 字节。
  • 精度:约 7 位有效数字。

FLOAT(4,2) 的含义

  • 整数部分最多 4 - 2 = 2 位。
  • 小数部分固定 2 位。
  • 范围:-99.99 ~ 99.99。
  • 当插入的值超过小数位数时,MySQL 会四舍五入。

示例

sql 复制代码
CREATE TABLE tt6 (id INT, salary FLOAT(4, 2));

-- 正常插入
INSERT INTO tt6 VALUES (100, -99.99);  -- 成功
INSERT INTO tt6 VALUES (101, -99.991);  -- 多出的小数被四舍五入

SELECT * FROM tt6;

输出:

bash 复制代码
+-----+--------+
| id  | salary |
+-----+--------+
| 100 | -99.99 |
| 101 | -99.99 |  -- -99.991 被四舍五入为 -99.99
+-----+--------+

无符号 FLOAT

sql 复制代码
CREATE TABLE tt7 (id INT, salary FLOAT(4, 2) UNSIGNED);

-- 无符号:范围是 0 ~ 99.99
INSERT INTO tt7 VALUES (100, -0.1);  -- 警告:转换为 0
INSERT INTO tt7 VALUES (100, 99.99);  -- 成功

SHOW WARNINGS;
DECIMAL 类型

语法

sql 复制代码
DECIMAL(M, D) [UNSIGNED]
  • M:最大位数,范围 1 ~ 65。
  • D:小数位数,范围 0 ~ 30。
  • 存储空间:可变,最多 17 字节。
  • 精度:精确的定点数,不存在浮点数误差。

DECIMAL 与 FLOAT 的区别

特性 FLOAT DECIMAL
存储方式 浮点数(近似) 定点数(精确)
精度 约 7 位有效数字 最多 65 位
存储空间 4 字节 可变,通常 5-17 字节
运算速度 较慢
使用场景 科学计算 财务数据、金额

DECIMAL 精度示例

sql 复制代码
CREATE TABLE tt8 (
    id INT, 
    salary_float FLOAT(10, 8), 
    salary_decimal DECIMAL(10, 8)
);

INSERT INTO tt8 VALUES (100, 23.12345612, 23.12345612);

SELECT * FROM tt8;

输出:

bash 复制代码
+-----+--------------+---------------+
| id  | salary_float | salary_decimal|
+-----+--------------+---------------+
| 100 | 23.12345695  | 23.12345612   |  -- DECIMAL 更精确
+-----+--------------+---------------+
浮点数类型选择建议
  • 财务数据(价格、金额) :必须用 DECIMAL。浮点数的舍入误差会在金钱计算中造成严重问题。
  • 科学计算 :可以用 FLOATDOUBLE
  • 一般数据 :如果有小数需求,优先选 DECIMAL

四、字符串类型

4.1 CHAR 类型(定长字符串)

语法
sql 复制代码
CHAR(L)
  • L:字符长度,范围 0 ~ 255。
  • 定长:无论实际存储多少字符,都占用指定长度的空间。
  • 存储空间:L 字节(UTF-8 编码下,如果字符是汉字,实际占用 3*L 字节)。
CHAR 示例
sql 复制代码
CREATE TABLE tt9 (id INT, name CHAR(2));

-- 插入英文字符
INSERT INTO tt9 VALUES (100, 'ab');

-- 插入汉字(在 UTF-8 编码下,2 个汉字占用 6 字节)
INSERT INTO tt9 VALUES (101, '中国');

SELECT * FROM tt9;

输出:

bash 复制代码
+-----+------+
| id  | name |
+-----+------+
| 100 | ab   |
| 101 | 中国 |
+-----+------+
CHAR 的特点
  • 固定空间CHAR(10) 永远占用 10 个字符的空间,即使只存 1 个字符。
  • 查询快:因为长度固定,数据库能直接定位到数据。
  • 空间浪费:如果实际数据远小于定义长度,会浪费空间。

4.2 VARCHAR 类型(变长字符串)

语法
sql 复制代码
VARCHAR(L)
  • L:最大字符长度,范围 0 ~ 65535。
  • 变长:只占用实际数据所需的空间,加上 1-3 字节的长度记录。
  • 存储空间:实际数据长度 + 1-3 字节。
VARCHAR 长度的计算

VARCHAR(L) 中的 L 代表字符数,但实际占用的字节数受表字符集影响:

  • UTF-8 编码 :1 个字符占 1-3 字节,行长度限制 65535 字节,所以 VARCHAR(L) 最大约为 65532 / 3 ≈ 21844。
  • GBK 编码 :1 个字符占 1-2 字节,行长度限制 65535 字节,所以 VARCHAR(L) 最大约为 65532 / 2 = 32766。
VARCHAR 示例
sql 复制代码
CREATE TABLE tt10 (id INT, name VARCHAR(6));

-- 插入英文
INSERT INTO tt10 VALUES (100, 'hello');

-- 插入汉字(4 个汉字,1 个标点符号 = 5 个字符)
INSERT INTO tt10 VALUES (101, '我爱,中国');

SELECT * FROM tt10;

输出:

bash 复制代码
+-----+--------------------+
| id  | name               |
+-----+--------------------+
| 100 | hello              |
| 101 | 我爱,中国       |
+-----+--------------------+
VARCHAR 长度验证

在 UTF-8 编码下,验证 VARCHAR 的最大长度:

sql 复制代码
-- 创建 VARCHAR(21845),会报错(超过限制)
CREATE TABLE tt11 (name VARCHAR(21845)) CHARSET=utf8;
ERROR 1118 (42000): Row size too large. The maximum row size for the used table type is 65535.

-- 创建 VARCHAR(21844),成功
CREATE TABLE tt11 (name VARCHAR(21844)) CHARSET=utf8;
Query OK, 0 rows affected (0.01 sec)

4.3 CHAR vs VARCHAR

特性 CHAR VARCHAR
长度 定长 变长
空间占用 固定,浪费 按需分配,节省
查询速度 快(直接定位) 稍慢(需要读取长度)
修改成本 高(可能需要重新分配)
最大长度 255 65535 (实际受表字符集限制)

如何选择

  • 长度固定且短 :使用 CHAR。比如:

    • 性别:CHAR(1)ENUM('M', 'F')
    • 国家代码:CHAR(2)
    • MD5 哈希:CHAR(32)
    • 身份证:CHAR(18)
  • 长度变化 :使用 VARCHAR。比如:

    • 用户名:VARCHAR(50)
    • 邮箱:VARCHAR(100)
    • 地址:VARCHAR(200)
    • 备注:VARCHAR(500)

五、日期时间类型

5.1 DATE 类型

格式YYYY-MM-DD

存储空间:3 字节

范围1000-01-01 ~ 9999-12-31

示例2024-04-07

5.2 DATETIME 类型

格式YYYY-MM-DD HH:MM:SS

存储空间:8 字节

范围1000-01-01 00:00:00 ~ 9999-12-31 23:59:59

示例2024-04-07 14:30:45

5.3 TIMESTAMP 类型

格式YYYY-MM-DD HH:MM:SS(与 DATETIME 相同)

存储空间:4 字节

范围1970-01-01 00:00:01 UTC ~ 2038-01-19 03:14:07 UTC

特点

  • 自动记录当前时间戳。
  • 创建记录时自动设为当前时间。
  • 更新记录时自动更新为当前时间。

5.4 日期时间类型示例

sql 复制代码
CREATE TABLE birthday (
    t1 DATE,
    t2 DATETIME,
    t3 TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 插入数据
INSERT INTO birthday (t1, t2) VALUES ('1997-07-01', '2008-08-08 12:01:01');

SELECT * FROM birthday;

输出:

bash 复制代码
+------------+---------------------+---------------------+
| t1         | t2                  | t3                  |
+------------+---------------------+---------------------+
| 1997-07-01 | 2008-08-08 12:01:01 | 2024-04-07 14:35:22 |  -- t3 自动设为当前时间
+------------+---------------------+---------------------+

更新数据

sql 复制代码
UPDATE birthday SET t1 = '2000-01-01';

SELECT * FROM birthday;

输出:

bash 复制代码
+------------+---------------------+---------------------+
| t1         | t2                  | t3                  |
+------------+---------------------+---------------------+
| 2000-01-01 | 2008-08-08 12:01:01 | 2024-04-07 14:36:10 |  -- t3 自动更新为当前时间
+------------+---------------------+---------------------+

5.5 日期时间类型选择建议

类型 使用场景 说明
DATE 生日、出生日期 只需要日期,不需要时间
DATETIME 订单时间、发布时间 需要精确到秒的时间戳
TIMESTAMP 创建时间、更新时间 自动记录操作时间,节省空间

实际应用

sql 复制代码
CREATE TABLE articles (
    id INT AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(100),
    content TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    published_date DATE COMMENT '发布日期'
);

在这个例子中:

  • created_at 记录文章创建的精确时间。
  • updated_at 每次修改文章时自动更新。
  • published_date 只记录发布的日期。

六、枚举和集合

6.1 ENUM 类型(单选)

语法
sql 复制代码
ENUM('value1', 'value2', 'value3', ...)

特点

  • 只能选择一个值。
  • 内部用数字存储(1, 2, 3, ...)以节省空间。
  • 最多 65535 个选项。
ENUM 示例
sql 复制代码
CREATE TABLE users (
    id INT,
    name VARCHAR(30),
    gender ENUM('male', 'female', 'other')
);

-- 插入时使用字符串值
INSERT INTO users VALUES (1, 'Alice', 'female');
INSERT INTO users VALUES (2, 'Bob', 'male');

-- 也可以使用数字索引(1-based)
INSERT INTO users VALUES (3, 'Tom', 1);  -- 1 对应 'male'

SELECT * FROM users;

输出:

bash 复制代码
+----+-------+--------+
| id | name  | gender |
+----+-------+--------+
| 1  | Alice | female |
| 2  | Bob   | male   |
| 3  | Tom   | male   |
+----+-------+--------+

6.2 SET 类型(多选)

语法
sql 复制代码
SET('value1', 'value2', 'value3', ...)

特点

  • 可以选择多个值,用逗号分隔。
  • 内部用位掩码存储(1, 2, 4, 8, 16, 32, ...)。
  • 最多 64 个选项。
SET 示例

场景:调查用户的兴趣爱好。

sql 复制代码
CREATE TABLE votes (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(30),
    hobby SET('登山', '游泳', '篮球', '武术'),
    gender ENUM('男', '女')
);

-- 插入单个爱好
INSERT INTO votes (username, hobby, gender) VALUES ('李磊', '登山', '男');

-- 插入多个爱好(用逗号分隔)
INSERT INTO votes (username, hobby, gender) VALUES ('雷锋', '登山,武术', '男');
INSERT INTO votes (username, hobby, gender) VALUES ('Juse', '登山,武术', '女');

-- 也可以用数字表示(2的幂):登山=1, 游泳=2, 篮球=4, 武术=8
-- 登山+武术 = 1+8 = 9
INSERT INTO votes (username, hobby, gender) VALUES ('小明', 9, '男');

SELECT * FROM votes;

输出:

bash 复制代码
+-----------+---------------+--------+
| username  | hobby         | gender |
+-----------+---------------+--------+
| 李磊      | 登山          | 男     |
| 雷锋      | 登山,武术     | 男     |
| Juse      | 登山,武术     | 女     |
| 小明      | 登山,武术     | 男     |
+-----------+---------------+--------+

6.3 SET 的查询

简单查询(不推荐)

直接查询会有问题:

sql 复制代码
SELECT * FROM votes WHERE hobby = '登山';

输出:

bash 复制代码
+-----------+-------+--------+
| username  | hobby | gender |
+-----------+-------+--------+
| 李磊      | 登山  | 男     |
+-----------+-------+--------+

这个查询只返回恰好等于 '登山' 的记录,多选的记录被漏掉了。

使用 FIND_IN_SET 函数

FIND_IN_SET(sub, str_list) 函数返回子字符串在字符串列表中的位置,如果不存在返回 0。

查询所有喜欢登山的人(包括登山和其他爱好的组合):

sql 复制代码
SELECT * FROM votes WHERE FIND_IN_SET('登山', hobby);

输出:

bash 复制代码
+-----------+---------------+--------+
| username  | hobby         | gender |
+-----------+---------------+--------+
| 李磊      | 登山          | 男     |
| 雷锋      | 登山,武术     | 男     |
| Juse      | 登山,武术     | 女     |
| 小明      | 登山,武术     | 男     |
+-----------+---------------+--------+

FIND_IN_SET 详解

sql 复制代码
-- 返回 1('a' 在位置 1)
SELECT FIND_IN_SET('a', 'a,b,c');

-- 返回 0('d' 不在列表中)
SELECT FIND_IN_SET('d', 'a,b,c');

输出:

bash 复制代码
+---------------------------+
| FIND_IN_SET('a', 'a,b,c') |
+---------------------------+
| 1                         |
+---------------------------+

+---------------------------+
| FIND_IN_SET('d', 'a,b,c') |
+---------------------------+
| 0                         |
+---------------------------+

WHERE 子句中使用时,FIND_IN_SET 返回非零值就表示匹配:

sql 复制代码
SELECT * FROM votes WHERE FIND_IN_SET('篮球', hobby);

输出所有包含篮球爱好的人。

6.4 ENUM 和 SET 的对比

特性 ENUM SET
选择数量 单选 多选
存储方式 数字索引(1, 2, 3, ...) 位掩码(1, 2, 4, 8, ...)
最多选项 65535 64
查询方式 直接相等比较 需要 FIND_IN_SET 函数
空间占用 1-2 字节 1-8 字节
使用场景 性别、状态、级别 权限、标签、兴趣爱好

6.5 ENUM 和 SET 的使用建议

使用 ENUM 的场景

  • 用户性别:ENUM('male', 'female', 'other')
  • 订单状态:ENUM('pending', 'paid', 'shipped', 'delivered')
  • 用户等级:ENUM('bronze', 'silver', 'gold', 'platinum')

使用 SET 的场景

  • 用户权限:SET('read', 'write', 'delete', 'admin')
  • 商品标签:SET('新品', '热销', '折扣', '推荐')
  • 课程类型:SET('视频', '资料', '作业', '讨论')

避免过度使用

  • ENUM 和 SET 虽然节省空间,但查询逻辑复杂。
  • 如果需要频繁查询这些字段,考虑用关联表代替。
  • 修改选项时(添加或删除选项)需要修改表结构,成本较高。

七、其他字符串类型

7.1 TEXT 类型

语法

sql 复制代码
TEXT

存储空间:最多 65535 字节(约 64 KB)

使用场景

  • 文章内容
  • 评论文本
  • 产品描述
  • 任何超过 VARCHAR 能存储的长文本

特点

  • 不能指定长度。
  • 不能有默认值。
  • 查询性能比 VARCHAR 稍低(不能建立普通索引,只能全文索引)。

7.2 BLOB 类型

语法

sql 复制代码
BLOB

存储空间:最多 65535 字节

使用场景

  • 二进制数据(图片、视频、音频的二进制内容)
  • 序列化对象
  • 加密数据

特点

  • 用于存储二进制数据,而非文本。
  • 不涉及字符集或校验规则。

八、数据类型选择指南

8.1 完整的数据类型对比表

数据类型 存储空间 范围 使用场景
TINYINT 1 字节 -128 ~ 127 或 0 ~ 255 布尔标记、状态
INT 4 字节 -2^31 ~ 2^31-1 通常整数
BIGINT 8 字节 -2^63 ~ 2^63-1 用户 ID、时间戳
FLOAT 4 字节 约 ±3.4×10^38 不要用,精度不足
DECIMAL(M,D) 5-17 字节 精确小数 金额、价格(首选)
CHAR(L) L 字节 最多 255 字符 固定长度(身份证、MD5)
VARCHAR(L) L+1~3 字节 最多 65535 字节 可变长度(名字、邮箱)
TEXT 最多 64 KB 大段文本 文章、备注
DATE 3 字节 1000-9999 年 日期
DATETIME 8 字节 1000-9999 年 日期+时间
TIMESTAMP 4 字节 1970-2038 年 自动时间戳
ENUM 1-2 字节 最多 65535 个 单选(性别、状态)
SET 1-8 字节 最多 64 个 多选(权限、标签)

8.2 常见表设计示例

用户表
sql 复制代码
CREATE TABLE users (
    id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '用户ID',
    username VARCHAR(50) NOT NULL UNIQUE COMMENT '用户名',
    email VARCHAR(100) NOT NULL COMMENT '邮箱',
    password_hash CHAR(32) NOT NULL COMMENT 'MD5密码哈希',
    age TINYINT UNSIGNED COMMENT '年龄',
    gender ENUM('male', 'female', 'other') COMMENT '性别',
    phone CHAR(11) COMMENT '手机号',
    status TINYINT DEFAULT 1 COMMENT '状态:1=活跃,0=禁用',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
) ENGINE=InnoDB CHARSET=utf8mb4;
商品表
sql 复制代码
CREATE TABLE products (
    id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '商品ID',
    name VARCHAR(100) NOT NULL COMMENT '商品名称',
    description TEXT COMMENT '商品描述',
    price DECIMAL(10, 2) NOT NULL COMMENT '价格',
    stock INT DEFAULT 0 COMMENT '库存',
    category VARCHAR(50) COMMENT '分类',
    tags SET('新品', '热销', '折扣', '推荐') COMMENT '标签',
    image_url VARCHAR(255) COMMENT '图片URL',
    is_active TINYINT(1) DEFAULT 1 COMMENT '是否上架',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB CHARSET=utf8mb4;
订单表
sql 复制代码
CREATE TABLE orders (
    id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '订单ID',
    user_id BIGINT NOT NULL COMMENT '用户ID',
    total_amount DECIMAL(10, 2) NOT NULL COMMENT '订单总金额',
    status ENUM('pending', 'paid', 'shipped', 'delivered', 'cancelled') DEFAULT 'pending' COMMENT '订单状态',
    payment_method ENUM('credit_card', 'paypal', 'wechat', 'alipay') COMMENT '支付方式',
    delivery_date DATE COMMENT '配送日期',
    remarks VARCHAR(500) COMMENT '备注',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
) ENGINE=InnoDB CHARSET=utf8mb4;

8.3 设计表时的常见错误

❌ 错误做法一:过度使用 UNSIGNED

sql 复制代码
-- 不推荐
CREATE TABLE users (
    id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY
);

✅ 正确做法

sql 复制代码
-- 推荐
CREATE TABLE users (
    id BIGINT AUTO_INCREMENT PRIMARY KEY
);

理由:UNSIGNED 最多只能存 2^32,而 BIGINT 能存 2^63。BIGINT 不会比 UNSIGNED INT 多占多少空间,但更安全。

❌ 错误做法二:用 FLOAT 存储金额

sql 复制代码
-- 不推荐
CREATE TABLE products (
    price FLOAT
);

✅ 正确做法

sql 复制代码
-- 推荐
CREATE TABLE products (
    price DECIMAL(10, 2)
);

理由:FLOAT 有精度问题,会导致金钱计算错误。

❌ 错误做法三:VARCHAR 长度设置太小

sql 复制代码
-- 不推荐
CREATE TABLE users (
    email VARCHAR(20)  -- 太短,无法存储长邮箱
);

✅ 正确做法

sql 复制代码
-- 推荐
CREATE TABLE users (
    email VARCHAR(100)  -- 足够大,留有余量
);

理由:VARCHAR 是按需分配空间,设置大一点不浪费。

❌ 错误做法四:用 CHAR 存储变长数据

sql 复制代码
-- 不推荐
CREATE TABLE articles (
    title CHAR(100)  -- 大多数标题远小于 100,浪费空间
);

✅ 正确做法

sql 复制代码
-- 推荐
CREATE TABLE articles (
    title VARCHAR(200)  -- 变长,节省空间
);

九、总结与最佳实践

现在你已经掌握了:

数值类型:TINYINT、INT、BIGINT 的范围和使用场景

浮点数:FLOAT vs DECIMAL 的精度差异,财务数据必用 DECIMAL

字符串:CHAR 定长快但浪费,VARCHAR 变长省空间但稍慢

日期时间:DATE、DATETIME、TIMESTAMP 的区别和选择

特殊类型:ENUM(单选)和 SET(多选)的查询方式

完整案例:用户表、商品表、订单表的设计示例

核心建议

  1. 字符集统一用 UTF-8MB4:支持所有语言和 emoji。
  2. 整数用 BIGINT:避免 UNSIGNED 导致的溢出风险。
  3. 金额必用 DECIMAL:FLOAT 精度不足会造成严重问题。
  4. 字符串合理选择长度:VARCHAR 设置要足够大,CHAR 用于固定长度。
  5. 日期时间用 TIMESTAMP:自动记录创建和更新时间。
  6. ENUM/SET 要合理使用:不要过度设计,修改选项时要改表结构。

建议练习

设计一个博客系统的数据库,需要以下表:

  • users:用户信息
  • articles:文章内容
  • comments:评论
  • tags:标签
  • categories:分类

为每个表设计合适的字段和数据类型。


下一篇,我们将学习表的约束:PRIMARY KEY(主键)、NOT NULL、UNIQUE、FOREIGN KEY(外键)等。约束是确保数据完整性和一致性的关键机制。

相关推荐
一 乐4 小时前
酒店预订|基于springboot + vue酒店预订系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·论文·毕设·酒店预订系统
弹简特4 小时前
【APP测试-完结】05-App专项测试-ADB连接真机+Monkey测试
adb·monkey测试
光泽雨5 小时前
UNION 和 UNION ALL 作用
数据库·sql
heimeiyingwang5 小时前
【架构实战】SQL调优实战:从执行计划到索引优化
数据库·sql·架构
恼书:-(空寄5 小时前
分库分表风险应对手册(生产实战版)
数据库·分库分表
2401_885885045 小时前
开发视频短信接口好开发吗?图文视频短信接口对接教程
android·音视频
XDHCOM5 小时前
ORA-06521: PL/SQL映射函数错误,权威解析Oracle报错故障修复与远程处理方案
数据库·sql·oracle
仲芒5 小时前
[24年单独笔记] MySQL 常用的 DDL 命令
笔记·mysql·oracle
数厘5 小时前
2.11 约束的使用(主键、外键、非空、唯一、默认值约束)
sql·mysql·数据分析