乐途乐途
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
  • 学习路径
  • 第1章 数据库基础与安装

    • MySQL 简介
    • MySQL 5.6 到 5.7 到 8.0 关键差异速查
    • 安装 MySQL 5.7
    • 连接与断开服务器
    • 创建数据库
    • 创建数据表
    • 数据库与数据表
    • 加载数据
    • 获取数据库信息
    • 批处理模式
    • SHOW 语句汇总
    • FLUSH 与 RESET 语句
    • my.cnf 核心参数
    • 字符集与排序规则
  • 第2章 SQL基础查询

    • SELECT
    • WHERE
    • ORDER BY
    • LIMIT
    • COUNT
    • 聚合函数
    • 比较运算符
    • 逻辑运算符
    • 算术运算符
    • 模式匹配
    • NULL 值处理
    • UPDATE
    • DELETE
    • REPLACE
    • SELECT INTO
  • 第3章 数据类型与运算符

    • 数值类型
    • 字符串类型
    • 日期时间类型
    • BIT 类型
    • ENUM 类型
    • SET 类型
    • JSON 类型
    • 类型转换
  • 第4章 函数与表达式

    • 字符串函数
    • 数值函数
    • 日期函数
    • 全文检索函数
  • 第5章 高级查询与子查询

    • JOIN
    • 子查询
    • UNION
    • GROUP BY
    • HAVING
    • DISTINCT
  • 第6章 表与索引

    • 数据定义语言
    • 修改表结构
    • 视图
    • 修改视图与检查选项
    • 外键
    • 索引
    • 唯一索引
    • 复合索引
    • 存储引擎对比
    • 分区表
    • 第一范式与第二范式
    • 第三范式与 BC 范式
    • 反范式设计
  • 第7章 存储过程与函数

    • 存储过程
    • 存储函数
    • 变量
    • 流程控制
    • 游标
    • 预处理语句
  • 第8章 事务与锁

    • 事务
    • 事务隔离级别
    • 锁机制
    • MVCC
    • 死锁专题分析
    • LOCK TABLES
    • XA 事务
  • 第9章 用户管理与安全

    • 用户管理
    • 权限管理
    • 角色
    • SQL 注入防范
  • 第10章 性能优化入门

    • 执行计划
    • 索引优化
    • 查询优化
    • 查询优化器提示
    • 慢查询日志
    • InnoDB 深入机制
    • InnoDB 专项优化
    • Performance Schema
    • sys Schema
  • 第11章 复制与高可用

    • 主从复制原理
    • 半同步复制配置
    • binlog 开启与 point-in-time 恢复
    • mysqldump 全库备份
    • mysqldump 单表与条件备份
    • mysqldump 恢复与导入
    • xtrabackup 全量热备
    • xtrabackup 准备与恢复
    • xtrabackup 增量与流式备份
  • 第12章 触发器与事件

    • 触发器
    • 事件调度器
  • 参考

    • MySQL 5.7 专业术语大全
    • MySQL 5.7 关键字与保留字大全

日期时间类型

导学

日期时间是业务系统的"血脉"——订单时间、登录时间、有效期、生日……MySQL 5.7 提供多种日期时间类型,选错会导致时区混乱、范围溢出或精度不足。

定义

日期时间类型:用于存储日期、时间或日期时间组合的数据类型。MySQL 5.7 核心类型包括 DATE、TIME、DATETIME、TIMESTAMP 和 YEAR。

类型对比

类型格式范围存储空间时区转换
DATEYYYY-MM-DD1000-01-01 ~ 9999-12-313 字节否
TIMEHH:MM:SS-838:59:59 ~ 838:59:593 字节否
YEARYYYY1901 ~ 21551 字节否
DATETIMEYYYY-MM-DD HH:MM:SS1000-01-01 00:00:00 ~ 9999-12-31 23:59:595 字节(5.6+)否
TIMESTAMPYYYY-MM-DD HH:MM:SS1970-01-01 00:00:01 UTC ~ 2038-01-19 03:14:07 UTC4 字节是

DATETIME vs TIMESTAMP:最关键的选择

SQL 示例

场景一:DATETIME — 记录不变的时间点

常量演示:

SELECT
    '大翔' AS emp_name,
    '2026-01-10 09:00:00' AS event_time,
    YEAR('2026-01-10 09:00:00') AS year_val,
    DATE_FORMAT('2026-01-10 09:00:00', '%Y-%m-%d %H:%i') AS formatted
UNION ALL
SELECT
    '白歌',
    '2025-09-01 08:30:00',
    YEAR('2025-09-01 08:30:00'),
    DATE_FORMAT('2025-09-01 08:30:00', '%Y-%m-%d %H:%i');

结果:

emp_nameevent_timeyear_valformatted
大翔2026-01-10 09:00:0020262026-01-10 09:00
白歌2025-09-01 08:30:0020252025-09-01 08:30

结果解读:DATETIME 存储原始日期时间,不涉及时区转换。存储的是什么,查询出来就是什么。

适用:生日、创建时间(如果业务约定统一时区)、历史日期。

场景二:TIMESTAMP — 自动时区转换

当前数据状态:

CREATE TABLE employees (
    emp_id INT PRIMARY KEY AUTO_INCREMENT,
    emp_name VARCHAR(20),
    login_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '登录时间,自动转客户端时区'
);

INSERT INTO employees (emp_name) VALUES ('大翔');

当前表数据(假设会话时区为 +08:00):

emp_idemp_namelogin_time
1大翔2026-06-10 15:30:00

验证时区转换:

-- 查看当前会话时区
SELECT @@session.time_zone;

-- 将时区改为 UTC,再次查询
SET SESSION time_zone = '+00:00';
SELECT * FROM employees;

修改时区后的数据:

emp_idemp_namelogin_time
1大翔2026-06-10 07:30:00

结果解读:

  • 原始会话时区 +08:00 时,login_time 显示为 2026-06-10 15:30:00
  • 将时区改为 UTC(+00:00)后,同一行数据显示为 2026-06-10 07:30:00
  • 这说明 TIMESTAMP 在存储时转换为 UTC,查询时按会话时区转换回本地时间
  • DATETIME 不会有这种变化,存储和查询的值始终相同

特性:

  • 存储时转换为 UTC,查询时按会话时区转换回本地时间
  • 支持 DEFAULT CURRENT_TIMESTAMP 和 ON UPDATE CURRENT_TIMESTAMP
  • 范围受限于 2038 年(4 字节 Unix 时间戳的固有问题)

场景三:自动更新时间戳

当前数据状态:

CREATE TABLE employees (
    emp_id INT PRIMARY KEY AUTO_INCREMENT,
    emp_name VARCHAR(20),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
);

INSERT INTO employees (emp_name) VALUES ('大翔');

插入后表数据:

emp_idemp_namecreated_atupdated_at
1大翔2026-06-10 15:30:002026-06-10 15:30:00

更新操作:

UPDATE employees SET emp_name = '白歌' WHERE emp_id = 1;

SELECT * FROM employees;

更新后表数据:

emp_idemp_namecreated_atupdated_at
1白歌2026-06-10 15:30:002026-06-10 15:35:22

结果解读:

  • 插入时,created_at 和 updated_at 都自动设为当前时间
  • 更新行时,updated_at 自动刷新为当前时间,created_at 保持不变
  • 这是追踪记录变更时间的最佳实践

DATE、TIME、YEAR 类型演示

常量演示:

SELECT
    '大翔' AS emp_name,
    '1990-05-20' AS birth_date,
    '09:00:00' AS work_start,
    2012 AS grad_year,
    DATEDIFF('2026-06-10', '1990-05-20') AS days_alive,
    ADDTIME('09:00:00', '08:00:00') AS work_end
UNION ALL
SELECT
    '白歌',
    '1985-12-15',
    '08:30:00',
    2008,
    DATEDIFF('2026-06-10', '1985-12-15'),
    ADDTIME('08:30:00', '08:00:00');

结果:

emp_namebirth_datework_startgrad_yeardays_alivework_end
大翔1990-05-2009:00:0020121317017:00:00
白歌1985-12-1508:30:0020081478716:30:00

结果解读:

  • DATE 只存储日期部分,适合生日、纪念日等不需要时间的场景
  • TIME 只存储时间部分,适合班次、营业时长等场景
  • YEAR 只存储年份,仅占 1 字节,适合仅需年份的场景(如毕业年份)
  • DATEDIFF('2026-06-10', birth_date) 计算从出生到参考日期的总天数
  • ADDTIME(work_start, '08:00:00') 在上班时间上加 8 小时得到下班时间

TIMESTAMP 2038 问题演示

CREATE TABLE future_plans (
    plan_id INT PRIMARY KEY,
    expire_time TIMESTAMP COMMENT '保险到期时间'
);

-- 以下插入在严格模式下会失败
INSERT INTO future_plans VALUES (1, '2040-01-01 00:00:00');

结果解读:TIMESTAMP 使用 4 字节有符号整数存储从 1970-01-01 开始的秒数,最大值为 2147483647,对应 2038-01-19 03:14:07 UTC。2040-01-01 超出此范围,插入会失败或产生警告。存储未来时间(如保险到期日、合同有效期)必须使用 DATETIME。

常见误区

误区正解
TIMESTAMP 和 DATETIME 没区别TIMESTAMP 涉及时区转换,范围小(2038年问题);DATETIME 不转换时区,范围大。
用 VARCHAR 存日期无法利用日期函数(YEAR()、DATE_ADD 等),无法做范围查询优化,是极差的设计。
TIMESTAMP 的 2038 问题不严重对于存储未来时间(如 2040 年的保险到期日),TIMESTAMP 会溢出,必须用 DATETIME。
NOW() 和 CURRENT_TIMESTAMP 不同两者完全相同,NOW() 是函数,CURRENT_TIMESTAMP 是关键字常量。

面试考点

Q:DATETIME 和 TIMESTAMP 的区别?

  1. 范围:DATETIME 1000-9999 年,TIMESTAMP 1970-2038 年;2. 时区:TIMESTAMP 存储 UTC 并自动按会话时区转换,DATETIME 不转换;3. 空间:TIMESTAMP 4 字节,DATETIME 5 字节;4. 自动更新:TIMESTAMP 支持 DEFAULT CURRENT_TIMESTAMP 和 ON UPDATE。

Q:2038 年问题是什么?

TIMESTAMP 使用 4 字节有符号整数存储从 1970-01-01 开始的秒数,最大值为 2147483647,对应 2038-01-19 03:14:07 UTC。超过这个时间会溢出。DATETIME 没有此问题。

Q:如何查询某个月份的数据?

推荐 WHERE dt >= '2024-01-01' AND dt < '2024-02-01',可利用索引。避免 WHERE MONTH(dt) = 1,这会导致索引失效。

小结

  • 生日、历史日期用 DATE;纯时间用 TIME
  • 不需要时区转换的未来时间用 DATETIME
  • 需要时区自动转换、或需要自动更新机制用 TIMESTAMP
  • 绝不用字符串存储日期,会失去日期运算和索引优化的能力

下一章引子:不同数值类型和字符串类型之间经常需要相互转换,MySQL 提供了丰富的类型转换机制。

上一页
字符串类型
下一页
BIT 类型