WEEK 与 mode 参数是什么
MySQL 的 WEEK(date, mode) 与 YEARWEEK(date, mode) 按 mode 决定周编号规则:mode 同时控制「一周从周日还是周一开始」与「年初不足七天怎么算第 1 周」,可选值分 8 组语义。这是周报统计最容易出错的地方——同一日期在不同 mode 下可能差一周,跨年时更明显。PostgreSQL 用 EXTRACT(WEEK FROM d) 配合 EXTRACT(ISOYEAR FROM d) 取 ISO 周(周一为周首,含 1 月 4 日的那周为第 1 周),或用 date_trunc('week', d) 直接截到周一。
基本信息
| MySQL | WEEK(date[, mode]),不传 mode 时取 default_week_format 的值 |
|---|---|
| MySQL 年周组合 | YEARWEEK(date[, mode]),跨年时返回的年份可能属于上一年 |
| PostgreSQL | EXTRACT(WEEK FROM d) 与 EXTRACT(ISOYEAR FROM d) 均为 ISO 周口径 |
| PostgreSQL 截断 | date_trunc('week', d) 固定截到周一 |
| MySQL 取周一 | DATE_SUB(d, INTERVAL WEEKDAY(d) DAY) |
典型使用场景
- 周报与周环比统计,按周分组汇总订单量
- 财务周、业务周与自然周口径不同的报表
- 排班与值班表按周编排
- 与第三方报表对齐周编号,确认周首日与 ISO 口径
- 排查跨年时周编号跳变、同一周被拆成两周的问题
MySQL 写法
MySQL 写法
-- 同一日期在不同 mode 下可能落在不同的周
SELECT WEEK('2026-01-01', 0) AS w_mode0, # 周日为一周首,年初不足一周算第 0 周
WEEK('2026-01-01', 1) AS w_mode1, # 周一为一周首,含 4 天以上即算第 1 周
WEEK('2026-01-01', 3) AS w_mode3, # ISO 标准周编号
YEARWEEK('2026-01-01', 3) AS yw; # 年周组合
-- 按 ISO 周分组统计
SELECT YEARWEEK(created_at, 3) AS yw, COUNT(*) AS cnt
FROM orders
GROUP BY yw
ORDER BY yw;
-- 取某日期所在周的周一,作为周区间起点
SELECT DATE_SUB('2026-10-13', INTERVAL WEEKDAY('2026-10-13') DAY) AS monday;PostgreSQL 写法
PostgreSQL 写法
-- 按 ISO 周分组:年份取 ISOYEAR,避免跨年被算到错误年份
SELECT EXTRACT(ISOYEAR FROM created_at) AS iso_year,
EXTRACT(WEEK FROM created_at) AS iso_week,
count(*) AS cnt
FROM orders
GROUP BY 1, 2
ORDER BY 1, 2;
-- 直接截到周一,作为周区间起点
SELECT date_trunc('week', created_at)::date AS week_start, count(*)
FROM orders
GROUP BY 1
ORDER BY 1;用法对照
| 目标 | MySQL | PostgreSQL |
|---|---|---|
| 取周编号 | WEEK(d, mode) | EXTRACT(WEEK FROM d)(ISO) |
| 取年周组合 | YEARWEEK(d, mode) | EXTRACT(ISOYEAR…) 与 WEEK 组合 |
| 周首日 | 由 mode 决定(0 周日、1 周一) | date_trunc('week', d) 固定周一 |
| 按周分组 | GROUP BY YEARWEEK(d, 3) | GROUP BY date_trunc('week', d) |
MySQL 的 WEEK 在不传 mode 时会读 default_week_format 参数,不同实例的配置可能不一样;写周报 SQL 时必须显式传 mode,否则同一句 SQL 在不同环境的结果可能不同。
跨年周的编号规则必须与业务口径对齐:ISO 周下 1 月 1 日可能属于上一年最后一周,用「日期年份 + 周号」拼出来的键会和自然月报表对不上。
分组前先算周首日更省事:把日期截到周一(MySQL 用 DATE_SUB 减 WEEKDAY,PostgreSQL 用 date_trunc('week', d))后按该列分组,输出天然有序,也避开了周号跨年带来的对齐问题。