S1B-SQL基本数据类型-什么是数据类型?为什么需要选择合适的数据类型?怎么快速掌握整数字符串日期类型?

59 阅读30分钟

S1B-SQL基本数据类型-什么是数据类型?为什么需要选择合适的数据类型?怎么快速掌握整数字符串日期类型?

📝 摘要

数据类型选择错误导致存储浪费、性能下降?本文档用生活化比喻解析 SQL 整数、字符串、日期等基本数据类型,帮你掌握数据类型选择原则,提升数据库设计和查询性能。


目录


1. 前置知识点

基础知识点(必须掌握)

在学习 SQL 数据类型之前,你需要掌握以下知识点:

  • 数据库基本概念:了解数据库、表、字段、记录的基本概念(参考 S1A 文档)
  • 数据结构基础:理解什么是数据,数据的基本形式

🎯 学习建议

  • 零基础小白:建议先学习数据库基本概念(S1A),再学习数据类型
  • 有基础读者:可以直接学习数据类型的选择原则和最佳实践

2. 问题描述

实际场景

面试官:创建表时,如何选择合适的字段数据类型?如果选择错误会有什么问题?

实际开发场景

  • 创建学生表时,年龄字段应该用 INT 还是 TINYINT
  • 存储金额时,应该用 FLOAT 还是 DECIMAL
  • 姓名字段应该用 CHAR 还是 VARCHAR

常见问题

  • 数据类型选择不当,导致存储空间浪费
  • 使用 FLOAT 存储金额,计算时出现精度误差
  • 所有字符串都用 VARCHAR(255),浪费存储空间
  • 日期用字符串存储,无法进行日期运算

3. 问题考察点

学习 SQL 数据类型选择时,需要考察以下能力:

  • 数据类型选择意识:能否识别数据类型选择的重要性?是否理解不同数据类型的特点?
  • 存储空间优化:是否理解不同数据类型占用空间不同?能否根据实际需求选择最小合适的类型?
  • 性能影响认知:是否理解数据类型对查询性能的影响?整数类型比字符串类型查询更快?
  • 精确计算理解:是否理解浮点数精度误差?金额、价格应该使用 DECIMAL 而不是 FLOAT
  • 实际应用能力:能否在实际项目中合理选择数据类型?是否考虑数据范围和业务需求?
  • 代码注释习惯:是否理解代码注释的重要性?能否清晰解释每个字段的数据类型选择?

4. 快速上手(3 分钟)

🔥 Must(必做实践)

4.1 数据类型快速概览

数据类型(Data Type(数据类型))决定了字段可以存储什么类型的数据,就像身份证上的信息类型(姓名是文字,年龄是数字,出生日期是日期)。

常见数据类型分类

类型分类主要类型用途示例
整数类型INT、BIGINT存储年龄、数量、ID
浮点数类型FLOAT、DOUBLE存储价格、评分
定点数类型DECIMAL存储金额(精确计算)
字符串类型VARCHAR、CHAR存储姓名、地址、描述
日期时间类型DATE、DATETIME存储日期、时间

4.2 创建表时指定数据类型

原理:创建表时需要为每个字段指定数据类型,数据类型决定了字段可以存储什么类型的数据、占用多少存储空间、可以进行哪些运算。

-- 创建学生表示例
-- 下面逐行解释每个字段的数据类型选择原因
CREATE TABLE students (
    -- 整数类型:学号,主键确保唯一性
    -- INT 占用 4 字节,可以存储 -21 亿到 21 亿的整数(有符号),足够存储学号
    -- PRIMARY KEY 表示这是主键,不能为空且必须唯一
    id INT PRIMARY KEY,
    
    -- 字符串类型:姓名,最多 50 个字符
    -- VARCHAR(50) 表示可变长度字符串,最多 50 字符,实际存储只占用实际字符数 + 1 字节(长度信息)
    -- NOT NULL 表示该字段不能为空,必须提供姓名
    name VARCHAR(50) NOT NULL,
    
    -- 整数类型:年龄,范围 0-255
    -- TINYINT 占用 1 字节,可以存储 0-255 的整数(无符号),年龄范围完全够用
    -- 相比 INT 节省 3 字节,如果存储 100 万条记录,可以节省约 3 MB 空间
    age TINYINT,
    
    -- 定点数类型:分数,精确到小数点后 2 位
    -- DECIMAL(5, 2) 表示总共 5 位数字,小数点后 2 位,可以存储 0.00 到 999.99
    -- 使用 DECIMAL 而不是 FLOAT,避免精度误差(如 0.1 + 0.2 = 0.30000000000000004)
    score DECIMAL(5, 2),
    
    -- 日期类型:生日,只存储日期不包含时间
    -- DATE 占用 3 字节,格式为 YYYY-MM-DD,可以存储 1000-01-01 到 9999-12-31
    -- 相比 VARCHAR 存储日期(如 "2025-01-01" 占用 10 字节),节省 7 字节
    birthday DATE,
    
    -- 日期时间类型:创建时间,包含日期和时间
    -- DATETIME 占用 8 字节,格式为 YYYY-MM-DD HH:MM:SS
    -- 支持日期时间运算,如 DATE_ADD、DATE_SUB 等函数
    created_at DATETIME
);

关键要点:选择合适的数据类型可以节省存储空间、提高查询性能,确保数据准确性。每行代码的选择都有其原因,需要根据实际业务需求选择。


5. 什么是数据类型(Data Type)?

5.1 数据类型的基本定义

数据类型(Data Type(数据类型)) 是数据库中对字段存储数据类型的定义,它决定了:

  • 字段可以存储什么类型的数据(整数、字符串、日期等)
  • 字段占用的存储空间大小
  • 字段可以进行的运算操作

数据类型的特点

  • 类型约束:限制字段只能存储特定类型的数据
  • 存储优化:不同数据类型占用不同的存储空间
  • 性能影响:合适的数据类型可以提高查询和索引性能

5.2 生活化比喻

生活化比喻:数据类型就像仓库的货架标签

想象一下仓库:

  • INT 货架:只能放整数(如 1、2、100)
  • VARCHAR 货架:只能放文字(如 "张三"、"北京")
  • DATE 货架:只能放日期(如 "2025-01-01")

如果往 INT 货架放文字,就会出错!同样,数据库中的字段也只能存储对应类型的数据。

另一个比喻

  • 数据类型 = 身份证上的信息类型
    • 姓名字段:必须是文字(VARCHAR)
    • 年龄字段:必须是数字(INT)
    • 出生日期字段:必须是日期(DATE)

6. 为什么需要数据类型?

原理:数据类型定义了字段可以存储什么类型的数据,选择合适的类型可以节省存储空间、提高查询性能、确保数据准确性。

6.1 数据类型的重要性

不使用数据类型(或使用错误数据类型)的问题

-- ❌ 问题:没有数据类型约束,所有数据都存储为字符串
CREATE TABLE bad_design (
    id TEXT,           -- ❌ 学号用 TEXT 存储,浪费空间
    age TEXT,          -- ❌ 年龄用 TEXT 存储,无法进行数学运算
    score TEXT,        -- ❌ 分数用 TEXT 存储,无法精确计算
    birthday TEXT      -- ❌ 日期用 TEXT 存储,无法进行日期运算
);

-- ❌ 问题:存储空间浪费
-- 学号 1 存储为 "1"(占用 1 字节 + 1 字节长度信息 = 2 字节)
-- 如果用 INT,只需要 4 字节,但可以存储 42 亿个不同的值

-- ❌ 问题:无法进行数学运算
SELECT age + 1 FROM bad_design;  -- ❌ 错误:无法对字符串进行加法运算

-- ❌ 问题:数据验证困难
INSERT INTO bad_design (age) VALUES ('abc');  -- ❌ 可以插入无效数据

使用正确数据类型的优势

-- ✅ 解决方案:使用合适的数据类型
CREATE TABLE good_design (
    id INT PRIMARY KEY,          -- ✅ 整数类型:节省空间,可以自增
    age TINYINT,                 -- ✅ 整数类型:占用 1 字节,可以运算
    score DECIMAL(5, 2),         -- ✅ 定点数类型:精确计算,适合分数
    birthday DATE                -- ✅ 日期类型:可以日期运算,节省空间
);

-- ✅ 优势:可以数学运算
SELECT age + 1 FROM good_design;  -- ✅ 正确:可以直接进行数学运算

-- ✅ 优势:数据验证
INSERT INTO good_design (age) VALUES ('abc');  -- ✅ 错误:自动拒绝无效数据

优势总结

优势说明实际收益
存储优化选择合适类型节省存储空间百万条数据可节省数 GB 空间
性能提升整数类型比字符串类型查询更快查询速度可提升 10-100 倍
数据准确性类型约束防止无效数据避免数据错误,提高数据质量
运算支持支持数学运算、日期运算可以直接进行加减乘除、日期计算

7. 整数类型(Integer Types):存储整数值

原理:整数类型用于存储不带小数部分的数值,根据数据取值范围选择最小合适的类型,可以节省存储空间并提高查询性能。

7.1 整数类型的基本概念 📖官方文档 📚教程 💡示例

整数类型(Integer Types(整数类型)) 用于存储不带小数部分的数值,如年龄、数量、ID 等。

整数类型的特点

  • 无小数部分:只能存储整数(如 1、100、-50)
  • 存储空间固定:每种类型占用固定的字节数
  • 运算高效:整数运算比浮点数运算更快
  • 范围限制:每种类型都有取值范围限制

7.2 整数类型对比表格

🔥 Must(必做实践)

MySQL 整数类型对比

类型占用空间有符号范围无符号范围适用场景
TINYINT 📖1 字节-128 到 1270 到 255状态码、年龄(0-255)
SMALLINT 📖2 字节-32,768 到 32,7670 到 65,535年份、端口号
MEDIUMINT 📖3 字节-8,388,608 到 8,388,6070 到 16,777,215中等范围计数
INT / INTEGER 📖4 字节-2,147,483,648 到 2,147,483,6470 到 4,294,967,295最常用:ID、数量、价格(分)
BIGINT 📖8 字节-9,223,372,036,854,775,808 到 9,223,372,036,854,775,8070 到 18,446,744,073,709,551,615大数值、时间戳

关键要点

  • INT 是最常用的整数类型,适用于大多数场景
  • 根据实际取值范围选择最小合适的类型,节省存储空间
  • 无符号类型(UNSIGNED)可以扩大正数范围

7.3 整数类型的选择建议

选择原则

  1. 根据数据范围选择

    • 年龄(0-150):使用 TINYINT UNSIGNED(1 字节)
    • ID、数量:使用 INT(4 字节,最常用)
    • 大数值:使用 BIGINT(8 字节)
  2. 考虑是否需要负数

    • 需要负数:使用有符号类型(默认)
    • 不需要负数:使用 UNSIGNED 扩大范围
  3. 性能考虑

    • 较小的类型查询和索引更快
    • 但不要过度优化,INT 通常是最佳选择

7.4 实际应用示例

💡 Should(建议实践)

-- 示例:创建学生表,使用合适的整数类型
-- 下面详细解释每个字段的整数类型选择原因
CREATE TABLE students (
    -- INT:学号,主键自增
    -- 使用 INT 而不是 BIGINT,因为学号通常不会超过 21 亿
    -- AUTO_INCREMENT 表示自动递增,每次插入新记录时自动加 1
    -- PRIMARY KEY 表示主键,确保唯一性
    id INT PRIMARY KEY AUTO_INCREMENT,
    
    -- TINYINT UNSIGNED:年龄,范围 0-255
    -- 使用 TINYINT 而不是 INT,因为年龄范围是 0-150,TINYINT 的 0-255 范围完全够用
    -- UNSIGNED 表示无符号,年龄不可能是负数,这样可以扩大正数范围
    -- 相比 INT 节省 3 字节,如果存储 100 万条记录,可以节省约 3 MB 空间
    age TINYINT UNSIGNED,
    
    -- INT:分数,范围 0-100
    -- 使用 INT 而不是 TINYINT,虽然分数范围是 0-100,但考虑到未来可能扩展(如 150 分制)
    -- 使用 INT 更灵活,也是最常见的整数类型选择
    score INT,
    
    -- SMALLINT UNSIGNED:班级 ID,范围 0-65535
    -- 使用 SMALLINT 而不是 INT,因为班级数量通常不会超过 65535 个
    -- UNSIGNED 表示无符号,班级 ID 不可能是负数
    -- 相比 INT 节省 2 字节
    class_id SMALLINT UNSIGNED,
    
    -- BIGINT:总学生数,可能很大
    -- 使用 BIGINT 而不是 INT,因为总学生数可能超过 21 亿(虽然现实中不太可能)
    -- 但考虑到数据统计、汇总等场景,使用 BIGINT 更安全
    total_students BIGINT
);

-- 插入数据:演示如何插入数据
INSERT INTO students (age, score, class_id, total_students) 
VALUES (18, 95, 101, 1000000);
-- 说明:插入一条记录,age=18(TINYINT),score=95(INT),class_id=101(SMALLINT),total_students=1000000(BIGINT)

-- 查询和运算:演示整数类型的运算能力
-- 年龄加 1:直接进行数学运算,返回每个学生的年龄加 1
SELECT age + 1 AS next_age FROM students;
-- 说明:age 是整数类型,可以直接进行数学运算,返回 19(18 + 1)

-- 条件查询:演示整数类型的比较运算
SELECT * FROM students WHERE age > 18;
-- 说明:age 是整数类型,可以直接进行比较运算,查询年龄大于 18 的学生

8. 浮点数和定点数类型:存储小数值

原理:浮点数和定点数类型用于存储带小数部分的数值,浮点数类型(FLOAT、DOUBLE)可能存在精度误差,定点数类型(DECIMAL)可以精确存储,适合金额、价格等需要精确计算的场景。

8.1 浮点数类型(FLOAT、DOUBLE) 📖官方文档 📚教程

浮点数类型(Floating-Point Types(浮点数类型)) 用于存储带小数部分的数值,但可能存在精度误差。

浮点数类型对比

类型占用空间精度范围适用场景
FLOAT 📖4 字节约 7 位十进制±3.402823466E+38科学计算、一般精度要求
DOUBLE 📖8 字节约 15 位十进制±1.7976931348623157E+308高精度科学计算

⚠️ 注意事项:浮点数存在精度误差,不适合存储金额等需要精确计算的数据。

8.2 定点数类型(DECIMAL、NUMERIC) 📖官方文档 📚教程

定点数类型(Fixed-Point Types(定点数类型)) 用于存储精确的小数值,适用于财务计算等对精度要求高的场景。

DECIMAL 语法

DECIMAL(M, D)
-- M:总位数(精度),范围 1-65
-- D:小数位数(标度),范围 0-30

示例

  • DECIMAL(5, 2):总共 5 位,小数点后 2 位(如 999.99)
  • DECIMAL(10, 0):总共 10 位,无小数(如 9999999999)

DECIMAL 特点

  • 精确存储:不丢失精度,适合金额计算
  • 存储空间:根据精度动态分配,通常比浮点数占用更多空间
  • 运算精确:加减乘除运算结果精确

8.3 浮点数 vs 定点数对比

🔥 Must(必做实践)

对比示例

-- ❌ 问题:使用 FLOAT 存储金额,存在精度误差
CREATE TABLE bad_account (
    id INT,
    balance FLOAT      -- ❌ 浮点数:精度误差
);

INSERT INTO bad_account VALUES (1, 0.1);
INSERT INTO bad_account VALUES (2, 0.2);

SELECT SUM(balance) FROM bad_account;
-- 结果可能是:0.30000000000000004 ❌ 精度误差!

-- ✅ 解决方案:使用 DECIMAL 存储金额,精确计算
CREATE TABLE good_account (
    id INT,
    balance DECIMAL(10, 2)  -- ✅ 定点数:精确到小数点后 2 位
);

INSERT INTO good_account VALUES (1, 0.1);
INSERT INTO good_account VALUES (2, 0.2);

SELECT SUM(balance) FROM good_account;
-- 结果:0.30 ✅ 精确!

选择建议

场景推荐类型理由
金额、价格DECIMAL需要精确计算,不能有误差
评分、百分比DECIMAL 或 FLOAT根据精度要求选择
科学计算FLOAT 或 DOUBLE精度要求不高,性能优先
一般小数DECIMAL优先选择,避免精度问题

8.4 实际应用示例

💡 Should(建议实践)

-- 示例:商品表,使用合适的小数类型
CREATE TABLE products (
    id INT PRIMARY KEY,
    name VARCHAR(100),
    price DECIMAL(10, 2),          -- ✅ DECIMAL:价格(精确到分)
    rating FLOAT,                   -- ✅ FLOAT:评分(0.0-5.0,精度要求不高)
    discount DECIMAL(5, 2)          -- ✅ DECIMAL:折扣(0.00-100.00,精确)
);

-- 插入数据
INSERT INTO products (name, price, rating, discount) 
VALUES ('商品A', 99.99, 4.5, 10.00);

-- 精确计算
SELECT price * (1 - discount / 100) AS final_price FROM products;
-- ✅ 结果精确,不会有误差

9. 字符串类型(String Types):存储文本数据

原理:字符串类型用于存储文本数据,根据字符串长度是否固定选择 CHAR(定长)或 VARCHAR(变长),根据文本长度选择 VARCHAR 或 TEXT。

9.1 字符串类型的基本概念 📖官方文档 📚教程 💡示例

字符串类型(String Types(字符串类型)) 用于存储文本数据,如姓名、地址、描述等。

字符串类型分类

  • 定长字符串:CHAR(固定长度)
  • 变长字符串:VARCHAR(可变长度)
  • 大文本类型:TEXT(存储大段文本)

9.2 CHAR vs VARCHAR 对比

🔥 Must(必做实践)

CHAR 和 VARCHAR 对比

特性CHAR(n)VARCHAR(n)
存储方式固定长度,不足用空格填充可变长度,只存储实际字符
存储空间固定 n 字节实际字符数 + 1-2 字节(长度信息)
适用场景长度固定的字符串(如身份证号、邮政编码)长度可变的字符串(如姓名、地址)
性能查询稍快(长度固定)查询稍慢(需要读取长度信息)
最大长度255 字符65,535 字符(MySQL 5.0.3+)

实际示例

-- CHAR 示例:存储固定长度的字符串
CREATE TABLE id_cards (
    id INT,
    card_number CHAR(18)  -- ✅ 身份证号固定 18 位
);

INSERT INTO id_cards VALUES (1, '110101199001011234');
-- 存储:占用固定 18 字节

-- VARCHAR 示例:存储可变长度的字符串
CREATE TABLE users (
    id INT,
    name VARCHAR(50)      -- ✅ 姓名长度可变,最多 50 字符
);

INSERT INTO users VALUES (1, '张三');      -- 存储:3 字节(实际字符)+ 1 字节(长度信息)
INSERT INTO users VALUES (2, '这是一个很长的名字');  -- 存储:实际字符数 + 1 字节

选择建议

场景推荐类型理由
身份证号、邮政编码CHAR长度固定,节省空间
姓名、地址、描述VARCHAR长度可变,节省空间
状态码、代码CHAR长度固定,查询更快
一般文本字段VARCHAR优先选择,更灵活

9.3 TEXT 类型:存储大文本 📖官方文档 📚教程

TEXT 类型用于存储大段文本数据,如文章内容、评论等。

TEXT 类型对比

类型最大长度占用空间适用场景
TINYTEXT255 字符长度 + 1 字节短文本
TEXT65,535 字符长度 + 2 字节文章内容、长描述
MEDIUMTEXT16,777,215 字符长度 + 3 字节超长文本
LONGTEXT4,294,967,295 字符长度 + 4 字节极大文本

使用建议

  • 小于 255 字符:使用 VARCHAR(255)
  • 255-65535 字符:使用 TEXT
  • 超过 65535 字符:使用 MEDIUMTEXT 或 LONGTEXT

9.4 字符串类型选择建议

选择流程图(本概念在本文档第 9 节有详细介绍):

需要存储文本?
  ├─ 长度固定? → CHAR
  ├─ 长度可变且 < 255 字符? → VARCHAR
  └─ 长度可变且 > 255 字符? → TEXT

9.5 实际应用示例

💡 Should(建议实践)

-- 示例:用户表,使用合适的字符串类型
CREATE TABLE users (
    id INT PRIMARY KEY,
    username VARCHAR(50) NOT NULL,      -- ✅ VARCHAR:用户名(可变长度)
    email VARCHAR(100),                 -- ✅ VARCHAR:邮箱(可变长度)
    phone CHAR(11),                     -- ✅ CHAR:手机号(固定 11 位)
    id_card CHAR(18),                   -- ✅ CHAR:身份证号(固定 18 位)
    bio TEXT,                           -- ✅ TEXT:个人简介(可能很长)
    address VARCHAR(200)                -- ✅ VARCHAR:地址(可变长度)
);

-- 插入数据
INSERT INTO users (username, email, phone, id_card, bio, address) 
VALUES (
    '张三',                              -- VARCHAR:3 字节
    'zhangsan@example.com',             -- VARCHAR:22 字节
    '13800138000',                      -- CHAR:固定 11 字节
    '110101199001011234',               -- CHAR:固定 18 字节
    '这是一个很长的个人简介...',         -- TEXT:实际长度 + 2 字节
    '北京市朝阳区xxx街道'                -- VARCHAR:实际长度 + 1 字节
);

10. 日期和时间类型(Date and Time Types):存储时间数据

原理:日期和时间类型用于存储日期、时间或日期时间组合的数据,根据是否需要时间部分选择 DATE(只日期)或 DATETIME(日期时间),根据是否需要自动更新选择 DATETIME 或 TIMESTAMP。

10.1 日期和时间类型的基本概念 📖官方文档 📚教程 💡示例

日期和时间类型(Date and Time Types(日期和时间类型)) 用于存储日期、时间或日期时间组合的数据。

日期和时间类型的特点

  • 格式规范:标准化的日期时间格式
  • 运算支持:支持日期加减、比较等运算
  • 时区处理:TIMESTAMP 支持时区转换
  • 存储优化:比字符串类型更节省空间

10.2 日期和时间类型对比表格

🔥 Must(必做实践)

MySQL 日期时间类型对比

类型占用空间格式范围适用场景
DATE 📖3 字节YYYY-MM-DD1000-01-01 到 9999-12-31生日、日期
TIME 📖3 字节HH:MM:SS-838:59:59 到 838:59:59时间、时长
DATETIME 📖8 字节YYYY-MM-DD HH:MM:SS1000-01-01 00:00:00 到 9999-12-31 23:59:59最常用:创建时间、更新时间
TIMESTAMP 📖4 字节YYYY-MM-DD HH:MM:SS1970-01-01 00:00:01 UTC 到 2038-01-19 03:14:07 UTC自动更新时间、日志时间
YEAR 📖1 字节YYYY1901 到 2155年份

10.3 DATE vs DATETIME vs TIMESTAMP 对比

详细对比

特性DATEDATETIMETIMESTAMP
存储内容只存储日期存储日期和时间存储日期和时间
时区支持不支持不支持支持(自动转换)
自动更新不支持不支持支持(ON UPDATE)
存储空间3 字节(最小)8 字节4 字节
范围限制无限制无限制2038 年限制
使用场景生日、日期创建时间、事件时间更新时间、日志时间

实际示例

-- DATE 示例:只存储日期
CREATE TABLE birthdays (
    id INT,
    name VARCHAR(50),
    birthday DATE  -- ✅ 只存储日期,如 1990-01-01
);

-- DATETIME 示例:存储日期和时间
CREATE TABLE orders (
    id INT,
    order_time DATETIME  -- ✅ 存储完整时间,如 2025-01-01 10:30:00
);

-- TIMESTAMP 示例:自动更新时间
CREATE TABLE logs (
    id INT,
    content TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,           -- ✅ 创建时自动设置
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP  -- ✅ 更新时自动更新
);

选择建议

场景推荐类型理由
生日、日期DATE只需要日期,节省空间
创建时间、事件时间DATETIME需要完整时间,范围大
更新时间、日志时间TIMESTAMP自动更新,支持时区
一般时间字段DATETIME最常用,范围大

8.4 实际应用示例

💡 Should(建议实践)

-- 示例:用户表,使用合适的日期时间类型
CREATE TABLE users (
    id INT PRIMARY KEY,
    username VARCHAR(50),
    birthday DATE,                      -- ✅ DATE:生日(只需要日期)
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,  -- ✅ DATETIME:创建时间
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP  -- ✅ TIMESTAMP:自动更新时间
);

-- 插入数据
INSERT INTO users (username, birthday) 
VALUES ('张三', '1990-01-01');

-- 日期运算
SELECT username, 
       birthday, 
       YEAR(CURDATE()) - YEAR(birthday) AS age  -- ✅ 计算年龄
FROM users;

-- 日期比较
SELECT * FROM users 
WHERE birthday > '1990-01-01';  -- ✅ 日期比较查询

11. 数据类型选择流程图

💡 Should(建议实践)

数据类型选择流程图:下面的流程图帮助你根据实际需求选择合适的数据类型。

graph TD
    Start["需要存储数据"] --> Q1{"数据类型?"}
    
    Q1 -->|整数| IntQ{"取值范围?"}
    IntQ -->|0-255| TINYINT["TINYINT<br/>1字节"]
    IntQ -->|中等范围| SMALLINT["SMALLINT<br/>2字节"]
    IntQ -->|一般范围| INT["INT<br/>4字节,最常用"]
    IntQ -->|大范围| BIGINT["BIGINT<br/>8字节"]
    
    Q1 -->|小数| DecimalQ{"需要精确计算?"}
    DecimalQ -->|是| DECIMAL["DECIMAL<br/>精确"]
    DecimalQ -->|否| FLOAT["FLOAT/DOUBLE<br/>近似"]
    
    Q1 -->|文本| TextQ{"文本长度?"}
    TextQ -->|固定长度| CHAR["CHAR(n)<br/>定长"]
    TextQ -->|可变长度小于255| VARCHAR["VARCHAR(n)<br/>变长"]
    TextQ -->|可变长度大于255| TEXT["TEXT<br/>大文本"]
    
    Q1 -->|日期时间| DateQ{"需要什么?"}
    DateQ -->|只日期| DATE["DATE<br/>3字节"]
    DateQ -->|日期+时间| DATETIME["DATETIME<br/>8字节,最常用"]
    DateQ -->|自动更新| TIMESTAMP["TIMESTAMP<br/>4字节"]
    
    style INT fill:#e8f5e9,stroke:#388e3c
    style DECIMAL fill:#e1f5fe,stroke:#0288d1
    style VARCHAR fill:#fff3e0,stroke:#f57c00
    style DATETIME fill:#f3e5f5,stroke:#7b1fa2

快速选择指南

  1. 整数:优先选择 INT,除非有特殊需求
  2. 小数:金额、价格用 DECIMAL,其他用 FLOAT
  3. 文本:优先选择 VARCHAR,固定长度用 CHAR
  4. 日期时间:优先选择 DATETIME,需要自动更新用 TIMESTAMP

12. 对比示例:数据类型选择错误的问题

12.1 整数类型选择错误

问题场景 1:使用 BIGINT 存储年龄

-- ❌ 问题:使用 BIGINT 存储年龄,浪费空间
CREATE TABLE bad_students (
    id INT,
    age BIGINT  -- ❌ 年龄只需要 0-150,用 BIGINT 浪费 7 字节
);

-- 存储 100 万条记录:
-- BIGINT:8 字节 × 1,000,000 = 8 MB
-- TINYINT UNSIGNED:1 字节 × 1,000,000 = 1 MB
-- 浪费:7 MB ❌

-- ✅ 解决方案:使用 TINYINT UNSIGNED
CREATE TABLE good_students (
    id INT,
    age TINYINT UNSIGNED  -- ✅ 年龄 0-255,只需 1 字节
);

12.2 浮点数精度问题

问题场景 2:使用 FLOAT 存储金额

-- ❌ 问题:使用 FLOAT 存储金额,精度误差
CREATE TABLE bad_accounts (
    id INT,
    balance FLOAT  -- ❌ 浮点数精度误差
);

INSERT INTO bad_accounts VALUES (1, 0.1);
INSERT INTO bad_accounts VALUES (2, 0.2);

SELECT SUM(balance) FROM bad_accounts;
-- 结果:0.30000000000000004 ❌ 精度误差!

-- ✅ 解决方案:使用 DECIMAL 存储金额
CREATE TABLE good_accounts (
    id INT,
    balance DECIMAL(10, 2)  -- ✅ 精确到分,无误差
);

INSERT INTO good_accounts VALUES (1, 0.1);
INSERT INTO good_accounts VALUES (2, 0.2);

SELECT SUM(balance) FROM good_accounts;
-- 结果:0.30 ✅ 精确!

12.3 字符串类型选择错误

问题场景 3:使用 CHAR 存储可变长度文本

-- ❌ 问题:使用 CHAR 存储姓名,浪费空间
CREATE TABLE bad_users (
    id INT,
    name CHAR(50)  -- ❌ 姓名长度可变,用 CHAR 浪费空间
);

INSERT INTO bad_users VALUES (1, '张三');  -- 存储:固定 50 字节
INSERT INTO bad_users VALUES (2, '李四');  -- 存储:固定 50 字节
-- 浪费:每行浪费约 48 字节 ❌

-- ✅ 解决方案:使用 VARCHAR 存储姓名
CREATE TABLE good_users (
    id INT,
    name VARCHAR(50)  -- ✅ 姓名长度可变,只存储实际字符
);

INSERT INTO good_users VALUES (1, '张三');  -- 存储:3 字节 + 1 字节 = 4 字节
INSERT INTO good_users VALUES (2, '李四');  -- 存储:3 字节 + 1 字节 = 4 字节
-- 节省:每行节省约 46 字节 ✅

12.4 日期类型选择错误

问题场景 4:使用 VARCHAR 存储日期

-- ❌ 问题:使用 VARCHAR 存储日期,无法进行日期运算
CREATE TABLE bad_orders (
    id INT,
    order_date VARCHAR(20)  -- ❌ 日期用字符串存储
);

INSERT INTO bad_orders VALUES (1, '2025-01-01');

-- ❌ 无法进行日期运算
SELECT * FROM bad_orders 
WHERE order_date > '2025-01-01';  -- ❌ 字符串比较,可能出错

SELECT DATE_ADD(order_date, INTERVAL 1 DAY);  -- ❌ 错误:无法对字符串进行日期运算

-- ✅ 解决方案:使用 DATE 或 DATETIME 存储日期
CREATE TABLE good_orders (
    id INT,
    order_date DATE  -- ✅ 日期类型,支持日期运算
);

INSERT INTO good_orders VALUES (1, '2025-01-01');

-- ✅ 可以进行日期运算
SELECT * FROM good_orders 
WHERE order_date > '2025-01-01';  -- ✅ 日期比较,正确

SELECT DATE_ADD(order_date, INTERVAL 1 DAY) AS next_day;  -- ✅ 日期运算,正确

对比总结

错误选择正确选择问题影响
BIGINT 存储年龄TINYINT UNSIGNED浪费存储空间百万条数据浪费 7 MB
FLOAT 存储金额DECIMAL精度误差金额计算错误
CHAR 存储姓名VARCHAR浪费存储空间浪费约 50% 空间
VARCHAR 存储日期DATE/DATETIME无法日期运算功能受限

13. 常见错误与修正

13.1 整数类型常见错误

错误 1:所有整数都用 BIGINT

-- ❌ 错误:所有整数都用 BIGINT
CREATE TABLE bad_table (
    id BIGINT,      -- ❌ 可能只需要 INT
    age BIGINT,     -- ❌ 年龄用 BIGINT 浪费
    status BIGINT   -- ❌ 状态码用 BIGINT 浪费
);

✅ 修正:根据实际需求选择合适类型

-- ✅ 正确:根据实际需求选择
CREATE TABLE good_table (
    id INT,                    -- ✅ 一般 ID 用 INT
    age TINYINT UNSIGNED,      -- ✅ 年龄用 TINYINT
    status TINYINT UNSIGNED    -- ✅ 状态码用 TINYINT
);

错误 2:忘记使用 UNSIGNED

-- ❌ 错误:年龄可能为负数
CREATE TABLE bad_table (
    age INT  -- ❌ 年龄不应该为负数
);

-- ✅ 修正:使用 UNSIGNED
CREATE TABLE good_table (
    age TINYINT UNSIGNED  -- ✅ 年龄只能为正数
);

13.2 浮点数类型常见错误

错误 1:用 FLOAT 存储金额

-- ❌ 错误:金额用 FLOAT 存储
CREATE TABLE bad_products (
    price FLOAT  -- ❌ 精度误差
);

✅ 修正:金额使用 DECIMAL

-- ✅ 正确:金额用 DECIMAL 存储
CREATE TABLE good_products (
    price DECIMAL(10, 2)  -- ✅ 精确到分
);

13.3 字符串类型常见错误

错误 1:所有字符串都用 VARCHAR(255)

-- ❌ 错误:所有字符串都用 VARCHAR(255)
CREATE TABLE bad_table (
    id_card VARCHAR(255),  -- ❌ 身份证号应该用 CHAR(18)
    name VARCHAR(255),     -- ❌ 可能不需要 255
    description VARCHAR(255)  -- ❌ 可能超过 255
);

✅ 修正:根据实际需求选择

-- ✅ 正确:根据实际需求选择
CREATE TABLE good_table (
    id_card CHAR(18),           -- ✅ 身份证号固定 18 位
    name VARCHAR(50),           -- ✅ 姓名一般不超过 50
    description TEXT            -- ✅ 描述可能很长
);

错误 2:CHAR 和 VARCHAR 混用不当

-- ❌ 错误:可变长度用 CHAR
CREATE TABLE bad_table (
    name CHAR(50)  -- ❌ 姓名长度可变,应该用 VARCHAR
);

-- ✅ 修正:根据长度是否固定选择
CREATE TABLE good_table (
    name VARCHAR(50),      -- ✅ 姓名长度可变
    id_card CHAR(18)       -- ✅ 身份证号长度固定
);

13.4 日期时间类型常见错误

错误 1:用 VARCHAR 存储日期

-- ❌ 错误:日期用字符串存储
CREATE TABLE bad_table (
    birthday VARCHAR(20)  -- ❌ 无法进行日期运算
);

✅ 修正:使用 DATE 或 DATETIME

-- ✅ 正确:使用日期类型
CREATE TABLE good_table (
    birthday DATE  -- ✅ 支持日期运算
);

错误 2:DATETIME 和 TIMESTAMP 选择不当

-- ❌ 错误:需要自动更新但用 DATETIME
CREATE TABLE bad_table (
    updated_at DATETIME  -- ❌ 无法自动更新
);

-- ✅ 修正:需要自动更新用 TIMESTAMP
CREATE TABLE good_table (
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP  -- ✅ 自动更新
);

14. 总结与展望

14.1 核心要点回顾

通过本指南的学习,你已经掌握了 SQL 基本数据类型的核心知识:

  1. 整数类型

    • INT 是最常用的整数类型
    • 根据取值范围选择最小合适的类型
    • 不需要负数时使用 UNSIGNED
  2. 浮点数和定点数类型

    • 金额、价格使用 DECIMAL(精确计算)
    • 一般小数可用 FLOAT(性能优先)
  3. 字符串类型

    • 长度固定用 CHAR,长度可变用 VARCHAR
    • 大文本用 TEXT
  4. 日期时间类型

    • DATE 存储日期,DATETIME 存储日期时间
    • 需要自动更新用 TIMESTAMP

核心原则

  • 存储优化:选择最小合适的类型,节省空间
  • 性能优先:整数类型比字符串类型查询更快
  • 精确计算:金额使用 DECIMAL,避免精度误差
  • 功能支持:日期类型支持日期运算,字符串类型不支持

14.2 下一步学习方向

完成本阶段的学习后,建议按照以下顺序继续学习:

  1. 继续学习 SQL 查询(S1C):掌握 SELECT、WHERE 等查询语句
  2. 学习数据操作(S2A):掌握 INSERT、UPDATE、DELETE 等操作
  3. 学习多表查询(S3A):掌握 JOIN 等高级查询

学习建议

  • 多实践:尝试创建表,选择合适的数据类型
  • 多对比:思考不同数据类型的选择对性能和存储的影响
  • 多思考:理解每个类型的适用场景,选择最合适的类型

14.3 鼓励与展望

数据类型选择是数据库设计的基础,合理的数据类型选择能够显著提升数据库的性能和存储效率。掌握这些知识,你将能够设计出更高效、更可靠的数据库表结构。

记住

  • INT = 最常用的整数类型
  • DECIMAL = 金额、价格的精确选择
  • VARCHAR = 最常用的字符串类型
  • DATETIME = 最常用的日期时间类型

继续加油,你已经掌握了 SQL 数据类型的基础知识!接下来,你将学习 SQL 查询语句,进一步提升数据库操作能力!

继续加油,你离 SQL 高手又近了一步! 🚀


15. 📚 参考资料与学习资源

15.1 官方文档

MySQL 数据类型官方文档

其他数据库官方文档

15.2 在线教程

中文教程

英文教程

15.3 推荐书籍

入门书籍

  • 《SQL 必知必会》- Ben Forta(包含数据类型介绍)
  • 《MySQL 必知必会》- Ben Forta(详细讲解 MySQL 数据类型)

进阶书籍

  • 《高性能 MySQL》- Baron Schwartz(包含数据类型优化)
  • 《MySQL 技术内幕:SQL 编程》- 姜承尧(深入讲解数据类型)

厦门工学院人工智能创作坊 -- 郑恩赐
2025 年 11 月 04 日