PostgreSQL 与 MySQL 时间类型避坑指南
从精度、索引、到 Java 入参的完整梳理
在日常开发中,时间字段的设计与查询往往最容易踩坑:精度到底选到秒还是毫秒?timestamp(0)
和 timestamp(3)
差多少?用 DATE_TRUNC
会不会让索引失效?Java 想按季度查询该怎么传参?本文基于 PostgreSQL 与 MySQL(5.7/8.0)最新行为,一次性把高频问题讲透。
1. 时间精度:秒、毫秒、微秒到底差在哪?
数据库 | 声明方式 | 存储精度 | 示例值 | 占用字节 |
---|---|---|---|---|
PostgreSQL | timestamp(0) | 秒 | 2025-07-28 15:30:00 | 8 |
PostgreSQL | timestamp(3) | 毫秒 | 2025-07-28 15:30:00.123 | 8 |
PostgreSQL | timestamp(6) | 微秒(默认) | 2025-07-28 15:30:00.123456 | 8 |
MySQL | timestamp(0) | 秒 | 2025-07-28 15:30:00 | 4 |
MySQL | timestamp(3) | 毫秒 | 2025-07-28 15:30:00.123 | 4+1=5 |
MySQL | timestamp(6) | 微秒 | 2025-07-28 15:30:00.123456 | 4+3=7 |
⚠️ 注意:
PostgreSQL 固定 8 字节,精度只影响“显示位”;
MySQL 的
fsp
(小数秒精度)会额外占用 0–3 字节。
2. 函数导致索引失效?牢记“左值原则”
无论 PostgreSQL 还是 MySQL,只要对索引列做了函数运算或隐式转换,B-Tree 索引都会失效。
写法 | 是否走索引 | 原因 |
---|---|---|
WHERE DATE_TRUNC('month', create_time) = '2025-07-01' | ❌ | 列被函数包裹 |
WHERE create_time >= '2025-07-01' AND create_time < '2025-08-01' | ✅ | 范围查询,列保持原样 |
推荐实践:
Java 直接传入
LocalDateTime
的起止时间,让数据库做范围匹配;如需“按季度/按月”统计,在 Java 代码中计算好起止时间,而不是在 SQL 中
DATE_TRUNC
。
3. Java 传入“季度”参数的两种姿势
✅ 推荐做法:起止时间范围
int year = 2025;
int quarter = 3; // Q3
LocalDateTime start =LocalDateTime.of(year, (quarter-1)*3 + 1, 1, 0, 0); // 2025-07-01 00:00
LocalDateTime end = start.plusMonths(3); // 2025-10-01 00:00// MyBatis / JPA 示例
@Select("SELECT * FROM orders " +"WHERE order_time >= #{start} " +"AND order_time < #{end}")
List<Order> findByQuarter(@Param("start") LocalDateTime start,@Param("end") LocalDateTime end);
索引友好
避免时区歧义
任何数据库通用
❌ 不推荐做法:直接传 “2025-Q3” 字符串
WHERE TO_CHAR(order_time, 'YYYY-"Q"Q') = '2025-Q3' -- PostgreSQL
无法使用索引
字符串比较容易出格式问题
4. timestamp(0) 与 timestamp(3) 实战差异
存储与四舍五入演示(MySQL 8.0)
CREATE TABLE t(t0 timestamp(0),t3 timestamp(3)
);INSERT INTO t VALUES ('2025-07-28 15:30:00.987');
SELECT * FROM t;
t0 | t3 |
---|---|
2025-07-28 15:30:01 | 2025-07-28 15:30:00.987 |
t0
直接四舍五入到秒;
t3
保留毫秒,不会四舍五入。
5. 额外注意:MySQL TIMESTAMP 的 2038 陷阱
TIMESTAMP
在 MySQL 中最大只到 2038-01-19 03:14:07(32 位 Unix 秒数上限);如果业务生命周期可能超过 2038,请改用
DATETIME
或 PostgreSQL 的timestamp
。
6. 小结:一句话记重点
场景 | 一句话建议 |
---|---|
精度 | 需要毫秒就 timestamp(3) ,只到秒就 timestamp(0) |
索引 | 不在列上套函数,范围查询最稳 |
季度参数 | Java 计算好起止 LocalDateTime ,直接传 |
2038 问题 | 长期数据用 DATETIME / PostgreSQL timestamp |
7.PG时间类型转换
SELECT
DATE_TRUNC('year', order_create_time)::date AS year_only, -- 2025-01-01
TO_CHAR(order_create_time, 'YYYY-MM') AS year_month, -- 2025-07
order_create_time::date AS year_month_day, -- 2025-07-28
TO_CHAR(order_create_time, 'YYYY-"Q"Q') AS year_quarter -- 2025-Q3
FROM your_table;