跳转到主内容
趣航编程网 - 趣学编程,启航技术之路!

SQL实现按周或季度动态分组_日期函数的深度应用

MySQL中WEEK()默认以周日为起点且第1周含1月1日,易致跨年周错乱;应改用WEEK(date,1)或YEARWEEK(date,1),后者返回202401格式避免年份混淆。 MySQL里用
WEEK()
分组为什么总少一周或多一周 因为
WEEK()
默认按周日为一周起点,且第1周定义为“包含1月1日的那一周”,和日常说的“周一到周日”或“自然周”不一致。比如2024-01-01是周一,但
WEEK('2024-01-01')
返回1,而
WEEK('2023-12-31')
返回53——这天其实属于2024年第1周,但年份还是2023。 实操建议: 用
WEEK(date, 1)
:第二个参数
1
表示周一为每周第一天,且第1周必须包含4个及以上周一(ISO标准),更符合业务习惯 别只靠
WEEK()
分组,得配合
YEARWEEK(date, 1)
——它返回形如
202401
的整数,能避免跨年周错乱 如果要“显示为2024-W01”这种格式,用
CONCAT(YEAR(date), '-W', LPAD(WEEK(date, 1), 2, '0'))
,但注意:跨年日期仍可能归属前一年,所以优先用
YEARWEEK()
PostgreSQL中
date_trunc('week')
返回的是周日还是周一 默认是周一。PostgreSQL的
date_trunc('week', date)
会截断到**该周的周一零点**(时区按当前session设置)。比如
date_trunc('week', '2024-01-06'::date)
(周六)返回
2024-01-01
,不是
2024-01-07
。 常见错误现象: 误以为截断后是周日,结果分组时把周一数据丢进上一周 没考虑时区,服务器用UTC、应用用东八区,导致
date_trunc
结果偏移一天 对
timestamptz
字段直接
date_trunc('week')
,结果按UTC算,而非本地时间 正确做法:显式转时区再截断,例如
date_trunc('week', (created_at AT TIME ZONE 'Asia/Shanghai')) AT TIME ZONE 'Asia/Shanghai'
SQL Server里
DATEPART(week, ...)
和
DATEPART(iso_week, ...)
的区别
DATEPART(week, ...)
用的是SQL Server默认的周计算规则(由
SET DATEFIRST
控制,默认7=周日),而
DATEPART(iso_week, ...)
强制走ISO 8601标准:周一为每周第一天,第1周是包含当年第一个周四的那周。 使用场景: 做BI报表、跨系统对账时,必须用
iso_week
,否则和MySQL/PostgreSQL的
YEARWEEK(..., 1)
或PostgreSQL的
date_trunc('week')
结果不一致
DATEPART(week, ...)
在老系统遗留逻辑里可能出现,但别在新需求里用——它依赖会话级设置,不可靠 组合年份要用
YEAR(date)
+
DATEPART(iso_week, date)
,但注意:12月最后一周可能属于下一年,所以推荐用
FORMAT(date, 'yyyy-MM-dd')
配合窗口函数推导ISO周边界 按季度分组时,
QUARTER()
函数在不同数据库里的陷阱 MySQL和PostgreSQL的
QUARTER(date)
都返回1~4,看起来安全,但问题出在“季度起止时间”的语义模糊上:业务常说的Q1是1月1日到3月31日,但有些分析场景需要按“财务季度”(比如财年从4月开始)或“滚动季度”(最近90天)。 容易踩的坑: 直接
GROUP BY QUARTER(date)
+
YEAR(date)
,结果2024-Q1包含2024-01-01 ~ 2024-03-31,但若某条记录是
2024-03-31 23:59:59.999
,毫秒精度下可能因时区或类型隐式转换被归入下季度 SQL Server没有
QUARTER()
函数,得写
(MONTH(date) + 2) / 3
,但整除在T-SQL里是向下取整,
MONTH=1
时
(1+2)/3 = 1
,没问题;可一旦
date
是
datetime2
且含纳秒,表达式依然有效,但可读性差 真正稳健的做法是生成季度边界:比如MySQL用
MAKEDATE(YEAR(date), 1) + INTERVAL (QUARTER(date)-1) QUARTER
算起始日,再
+ INTERVAL 1 QUARTER - INTERVAL 1 DAY
算结束日 复杂点在于:所有“动态分组”本质都是把连续时间轴切成离散桶,桶的边界定义比函数名更重要。很多人调对了函数,却没校验桶是否真的覆盖全量、无重叠、无空隙。

相关文章