1. 动手写日期函数前,先搞清楚列里装的是“时间点”还是“日历日”
写 SQL 查询时,很多看起来一模一样的日期条件,最后跑出来的数据却对不上,根子往往不在函数本身,而在表里的字段到底是什么类型、存的到底是哪个时区的时间点。日期函数只是工具,先看清数据表的“时钟表盘”,再去选函数,才不会越写越乱。
1.1 不同数据库里的日期时间类型,名字相似但容量不同
我最早用的 MySQL,后来切到 PostgreSQL,中间还维护过 SQL Server 和 Oracle 的报表查询。最大的感受是:同样的“日期”,在不同数据库里根本不是同一个概念。你写 DATE 列,MySQL 里只存年月日;Oracle 的 DATE 类型却能把时分秒全装进去。这直接决定了你用截断函数之后的结果长什么样。
| 数据库 | 常见日期时间类型 | 是否自带时区 | 关键注意事项 |
|---|---|---|---|
| MySQL | DATE、DATETIME、TIMESTAMP |
TIMESTAMP 有会话时区转换 |
DATETIME 不带时区,存进去是多少就返回多少 |
| SQL Server | date、datetime、datetime2、datetimeoffset |
datetimeoffset 带时区 |
老 datetime 精度和范围都有限,新库建议用 datetime2 |
| PostgreSQL | date、timestamp without time zone、timestamp with time zone |
timestamptz 带时区 |
不推荐用字符串保存日期,timestamptz 底层是 UTC 存储 |
| Oracle | DATE、TIMESTAMP、TIMESTAMP WITH TIME ZONE |
极少用自带类型存时区 | Oracle 的 DATE 等于其他数据库的 DATETIME,里面包含时间部分 |
为什么这段看起来像基础课的内容值得先说?因为不少同事跑来问我“为什么我 WHERE date = '2024-01-01' 查不到数”,结果一查,字段是 timestamp,库里那条数据是 2024-01-01 08:30:00。字符串和日期比较时数据库会隐式转换,但时间部分还在,自然匹配不上。这不是日期函数的锅,是“字段真身”没看清。
1.2 时区问题:同一个函数,不同区域跑出的“今天”不一致
做国内业务可能感受不深,一旦涉及海外用户、跨境订单,或者数据仓库里的时间统一存成 UTC,日期函数就会开始作妖。比如订单表里的 created_at 存的是 UTC 时间,你直接写 DATE(created_at) 去按自然日分组,北京时间早上 8 点之前产生的订单,会被归到前一天。
这类问题的处理方式有两种。一种是入库和查询统一约定用 UTC,展示层再去转换;另一种是在 SQL 里显式转换,比如 MySQL 环境:
sql复制-- 将 UTC 时间转换为东八区时间
SELECT DATE(CONVERT_TZ(created_at, '+00:00', '+08:00')) AS biz_date
FROM orders;
但要注意,CONVERT_TZ 依赖 MySQL 的时区表是否初始化,不是所有环境都能直接用。更稳妥的做法是应用层在写入前就把时间转成业务时区。SQL Server 里可以用 AT TIME ZONE,PostgreSQL 里则是比较规矩的 AT TIME ZONE 表达式。不要依赖“先取字符串再改时间”的土办法,那会让后续排序、比较、分组全部变得不可控。
1.3 “今天”到底是自然日,还是最近 24 小时
这是我在需求评审里反复确认的问题。你说“查今天的数据”,对方可能想要的是从今天 00:00:00 到今天 23:59:59 之间的自然日数据,也可能只是“最近 24 小时滚动数据”。这两种写法完全不同。
自然日是左闭右开区间:
sql复制WHERE created_at >= '2025-01-06 00:00:00'
AND created_at < '2025-01-07 00:00:00';
滚动 24 小时是:
sql复制WHERE created_at >= NOW() - INTERVAL 24 HOUR;
如果需求文档只写了“最近一天”,我一般会再问一句:按自然日算还是按滚动时距算。这种语义确认比背函数更省事,因为只要语义错了,后面所有日期函数组合都救不回来。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 取当前时间不要只背一个 now(),还要知道它“卡”在哪个时刻
“取当前时间”是日期函数里最常用的一类。大家张口就能说 NOW()、GETDATE()、SYSDATE,但这些函数返回值的含义是有细微差异的。尤其在定时任务、数据回刷场景里,如果没搞清楚取值时点,很容易把日期边界算错。
2.1 几个常用取当前时间的函数对照
先放一份我平时查得最多的对照表:
| 数据库 | 当前日期时间 | 当前日期 | 当前 UTC 时间 | 说明 |
|---|---|---|---|---|
| MySQL | NOW() |
CURDATE() |
UTC_TIMESTAMP() |
NOW() 是语句开始执行时的时间 |
| SQL Server | GETDATE() |
CAST(GETDATE() AS date) |
GETUTCDATE() |
新环境也可以看 SYSDATETIME() |
| PostgreSQL | NOW() 或 CURRENT_TIMESTAMP |
CURRENT_DATE |
CURRENT_TIMESTAMP AT TIME ZONE 'UTC' |
事务中多次调用 NOW() 值不变 |
| Oracle | SYSDATE |
TRUNC(SYSDATE) |
SYS_EXTRACT_UTC(SYSTIMESTAMP) |
CURRENT_DATE 返回会话时区当前日期 |
很多初学者以为 NOW() 会“实时变化”,其实 MySQL 和 PostgreSQL 里的 NOW() 在很大程度上是稳定的。MySQL 中,同一条 SQL 里多处使用 NOW(),返回的是同一条语句开始时的时间;PostgreSQ
