EXTRACT支持YEAR、MONTH、DAY、HOUR、MINUTE、SECOND等标准字段,PostgreSQL还扩展支持CENTURY、DECADE、DOW、DOY、EPOCH等;MySQL仅支持基础字段且不支持DOW或WEEK;字段名须全大写、无引号,大小写敏感,非标准名称会报错。

EXTRACT 能提取哪些时间单位
EXTRACT 只能提取预定义的时间字段,比如 YEAR、MONTH、DAY、HOUR、MINUTE、SECOND,不能提取“星期几”或“第几周”这种衍生信息(PostgreSQL 支持 DOW,但 MySQL 和 SQL Server 完全不支持 EXTRACT)。不同数据库对字段名大小写不敏感,但必须用标准名称,写成 year 或 YR 都会报错。
- PostgreSQL:支持
CENTURY、DECADE、DOW(周日=0)、DOY(年中第几天)等扩展字段 - MySQL:只支持
YEAR、MONTH、DAY、HOUR、MINUTE、SECOND,且函数名是EXTRACT(unit FROM date) - Oracle:语法同 MySQL,但
unit不加括号,如EXTRACT(YEAR FROM order_date) - SQL Server:没有
EXTRACT,得用DATEPART(YEAR, order_date)
MySQL 中 EXTRACT 的典型写法和常见错误
MySQL 要求 unit 必须是关键字,且必须用 FROM 连接日期表达式;漏掉 FROM 或把括号位置搞错,都会触发 ERROR 1630 (42000): FUNCTION xxx does not exist —— 看起来像函数不存在,其实是语法解析失败。
- 正确写法:
SELECT EXTRACT(YEAR FROM '2023-04-15'); - 错误写法:
EXTRACT('YEAR' FROM ...)(引号导致解析为字符串字面量) - 错误写法:
EXTRACT(YEAR, '2023-04-15')(逗号分隔是旧版 MySQL 的废弃语法,5.7+ 已移除) - 注意:如果字段是
NULL,EXTRACT返回NULL,不是 0 或空字符串
PostgreSQL 提取年月日时的类型和性能影响
EXTRACT 返回的是 double precision 类型(即使提取的是整数),这在做 GROUP BY 或 JOIN 时可能引发隐式类型转换,尤其和整数列比较时容易出错。例如 EXTRACT(YEAR FROM created_at) = 2023 是安全的,但 EXTRACT(YEAR FROM created_at) IN (2023, 2024) 在某些版本里会慢一截——因为无法走索引,而等价的 created_at >= '2023-01-01' AND created_at 可以命中 B-tree 索引。
- 想按年份分组统计?优先用范围条件 + 索引,别只依赖
EXTRACT(YEAR FROM ...) - 需要补零输出(如
04月)?EXTRACT不管格式,得配合TO_CHAR(created_at, 'MM')(PostgreSQL)或LPAD(EXTRACT(MONTH FROM ...), 2, '0')(MySQL) - 提取日期部分后做计算?比如“本月第几天”,直接用
EXTRACT(DAY FROM now())就行,别绕路转成字符串再截取
跨数据库兼容性替代方案
如果代码要同时跑在 MySQL 和 PostgreSQL 上,EXTRACT 写法差异小,但 SQL Server 和 SQLite 根本不认这个函数。与其硬套统一语法,不如封装一层逻辑:对年月日这类基础字段,用 YEAR(date_col)、MONTH(date_col) 这类更通用的函数(MySQL/SQL Server 支持),或者直接用日期范围过滤。
- SQLite 没有
EXTRACT,得用strftime('%Y', order_date)提取年份 - Oracle 用户注意:
EXTRACT不能用于DATE类型的小时以下精度(因为DATE不存秒),必须转成TIMESTAMP才能提SECOND - 真正需要兼容多库时,提取年月日最稳的方式不是函数,而是
date_col >= '2023-01-01' AND date_col 这种范围写法
EXTRACT 的坑不在功能本身,而在不同数据库对“单位”的定义和返回类型处理不一致,尤其是和索引、类型比较、NULL 值交互时,很容易表面跑通,线上查半天才暴露问题。


















