mysql中interval必须与date_add()或date_sub()配合使用,如date_add(now(), interval 1 day);单独写now() + interval 1 day会报error 1064,且interval后数值不能为字段或变量。

INTERVAL 在 MySQL 中的正确写法和常见报错
MySQL 的 INTERVAL 不是独立函数,而是配合 DATE_ADD() 或 DATE_SUB() 使用的关键字,单独写 SELECT NOW() + INTERVAL 1 DAY 会报错(如 ERROR 1064),因为 MySQL 不支持裸用 + 或 - 对日期加减间隔——必须显式调用日期函数。
正确姿势只有两种:
DATE_ADD(NOW(), INTERVAL 1 DAY)DATE_SUB(NOW(), INTERVAL 7 HOUR)
注意:INTERVAL 后面的数值不能是字段或变量(除非用预处理语句动态拼接),直接写 INTERVAL duration DAY 会报语法错误。
PostgreSQL 里没有 INTERVAL 关键字?其实是有的,但用法完全不同
PostgreSQL 支持直接用 + 和 - 运算符配合 INTERVAL 字面量,比如 NOW() + INTERVAL '3 months' 是合法且推荐的写法。这里 INTERVAL 是一个类型构造器,后面必须跟带引号的字符串(如 '2 days'、'1 year 6 months'),不能省略引号,也不能写成 INTERVAL 2 DAY(那是 MySQL 风格,PG 会报错 syntax error at or near "2")。
常见陷阱:
- 漏掉单引号 →
ERROR: syntax error at or near "2" - 单位大小写敏感?不敏感,
'2 DAYS'和'2 days'都行 - 想用列值动态控制间隔?得用
MAKE_INTERVAL()函数,例如NOW() + MAKE_INTERVAL(days => duration_col)
在 WHERE 条件中用 INTERVAL 做时间范围过滤时的性能注意点
用 WHERE created_at > DATE_SUB(NOW(), INTERVAL 30 DAY)(MySQL)或 WHERE created_at > NOW() - INTERVAL '30 days'(PG)本身没问题,但要注意:这类表达式无法利用 created_at 字段上的普通 B-Tree 索引做索引下推(optimizer 通常能处理,但某些旧版本或复杂嵌套下可能退化为全表扫描)。
更稳妥的做法是把计算移到右边:
- MySQL:
WHERE created_at > '2024-05-01 00:00:00'(提前算好常量) - PostgreSQL:
WHERE created_at > (NOW() - INTERVAL '30 days')::timestamp(确保类型明确)
如果 created_at 是 TIMESTAMP WITH TIME ZONE,PG 中还建议统一用 AT TIME ZONE 显式指定时区,避免夏令时导致边界偏移。
跨数据库兼容写法几乎不存在,别硬套
MySQL 的 INTERVAL 1 WEEK、PostgreSQL 的 INTERVAL '7 days'、SQL Server 的 DATEADD(week, 1, GETDATE())、SQLite 的 datetime('now', '+7 days') —— 四种语法完全不兼容。试图用宏或 ORM 抽象层“统一”写法,往往在时区处理、负数间隔、复合单位(如 '1 year 2 months')上翻车。
真正可行的方案只有两个:
- 业务层计算好时间戳再传入 SQL(适合简单偏移)
- 按目标数据库选型,写死对应方言,CI 中用真实 DB 实例跑集成测试验证
最易被忽略的是:不同数据库对 INTERVAL '1 month' 的解释逻辑不同——MySQL 按日历月推进(可能跳到下月31号),PG 也是日历月,但某些版本对月末日期的处理细节有差异。需要精确到日的场景,宁可用天数代替月数。











