首页 / 日期
MONTHS_BETWEEN 相差月数
MONTHS_BETWEEN(end, start) 返回两个日期相差的月数,带小数。MaxCompute、Doris、Hive 和 Spark 同名可用,小数部分都按每月 31 天折算,但对时间部分和舍入的处理不同。StarRocks 的 months_diff 和 Flink 的 TIMESTAMPDIFF 只返回整数月,Flink 的参数顺序还是反的。
各引擎写法
| 引擎 | 支持 | 写法 | 说明 | 依据 |
|---|---|---|---|---|
| MaxCompute | 支持 | MONTHS_BETWEEN(end_date, start_date) | 返回 DOUBLE,end_date 晚于 start_date 时为正。两个日期都是月末时返回整数,否则按天数差除以 31 折算。 | 官方文档 |
| Hologres | 需改写 | (EXTRACT(YEAR FROM a) - EXTRACT(YEAR FROM b)) * 12 + EXTRACT(MONTH FROM a) - EXTRACT(MONTH FROM b) | 日期函数中没有 MONTHS_BETWEEN,可以用 EXTRACT 取年和月计算整数月差,不含小数部分。 | 官方文档 |
| StarRocks | 需改写 | months_diff(expr1, expr2) | 函数列表中没有 MONTHS_BETWEEN。months_diff 返回 expr1 减 expr2 的月数,类型是 BIGINT,只精确到月;日期不存在时返回 NULL。 | 官方文档 |
| Doris | 支持 | MONTHS_BETWEEN(end, start[, round_type]) | 与 Oracle 的 MONTHS_BETWEEN 一致。时间部分不参与计算;两个日期同为月末或日相同时返回整数。round_type 为 true 时保留 8 位小数。 | 官方文档 |
| Hive | 支持 | months_between(date1, date2) | Hive 1.2.0 起提供。同一天或都是月末时返回整数,否则按 31 天折算并考虑时间部分,结果保留 8 位小数。 | 官方文档 |
| Spark SQL | 支持 | months_between(timestamp1, timestamp2[, roundOff]) | Spark 1.5.0 起提供。同一天或都是月末时忽略时间部分,否则按每月 31 天计算,默认保留 8 位小数,roundOff 为 false 时不舍入。 | 官方文档 |
| Flink SQL | 需改写 | TIMESTAMPDIFF(MONTH, start, end) | 没有 MONTHS_BETWEEN。TIMESTAMPDIFF 返回 timepoint2 减 timepoint1 的整数,参数顺序与 MONTHS_BETWEEN 相反。 | 官方文档 |
迁移注意事项
- Flink 的 TIMESTAMPDIFF(MONTH, t1, t2) 计算的是 t2 减 t1,开始时间在前;MONTHS_BETWEEN 是结束时间在前。照搬参数顺序会得到相反的符号。
- StarRocks 的 months_diff、Flink 的 TIMESTAMPDIFF 和 Hologres 的 EXTRACT 写法都只返回整数月,原 SQL 中依赖小数部分的计算(例如按 0.5 个月判断)需要重新设计。
- 时间部分的处理不同:Doris 只看日期,Hive 会把时间部分算进小数,Spark 在同一天或都是月末时忽略时间。同样的输入可能得到不同的小数。
- 舍入不同:Hive 和 Spark 默认保留 8 位小数,MaxCompute 返回完整的 DOUBLE,Doris 由 round_type 控制。和原结果做等值比较前,先统一 ROUND。
示例
计算用户注册至今的月数
SELECT user_id, MONTHS_BETWEEN(GETDATE(), register_time) AS months FROM users;SELECT user_id, (EXTRACT(YEAR FROM NOW()) - EXTRACT(YEAR FROM register_time)) * 12 + EXTRACT(MONTH FROM NOW()) - EXTRACT(MONTH FROM register_time) AS months FROM users;SELECT user_id, months_diff(NOW(), register_time) AS months FROM users;SELECT user_id, MONTHS_BETWEEN(NOW(), register_time) AS months FROM users;SELECT user_id, months_between(current_timestamp, register_time) AS months FROM users;SELECT user_id, months_between(current_timestamp(), register_time) AS months FROM users;SELECT user_id, TIMESTAMPDIFF(MONTH, register_time, CURRENT_TIMESTAMP) AS months FROM users;