weekday()返回周一为0、周日为6,dayofweek()返回周日为1、周六为7;需据公司作息选择函数,避免直接用weekday(date)导致周日误判。

用 WEEKDAY() 或 DAYOFWEEK() 判断工作日再分组
MySQL 中没有内置“工作日”类型,得靠函数把日期转成数字再人工定义。关键区别在于:WEEKDAY() 周一=0、周日=6;DAYOFWEEK() 周日=1、周六=7。选哪个取决于你公司是否把周日当工作日。
常见错误是直接用 WEEKDAY(date) 就认为是工作日——但没考虑法定节假日,也没处理跨年/跨月时的边界。实际业务中建议先建一张 <code>holiday 表存调休和放假日期,后续再 join 过滤。
示例(MySQL):
SELECT CASE WHEN WEEKDAY(order_date) <h3>PostgreSQL 里用 <code>EXTRACT(ISODOW FROM ...)</code> 更稳妥</h3><p>PostgreSQL 的 <code>EXTRACT(ISODOW FROM date)</code> 遵循 ISO 标准:周一=1、周日=7,语义清晰不易错。比用 <code>TO_CHAR(date, 'D')</code> 强——后者受 <code>lc_time</code> 设置影响,可能返回 1~7 或 0~6。</p><p>注意:如果表数据量大,对 <code>order_date</code> 字段加函数索引没用(<code>EXTRACT(ISODOW FROM order_date)</code> 无法走索引),得配合生成列或物化视图提前算好。</p><p>实操建议:</p>
- 建生成列:
ALTER TABLE orders ADD COLUMN day_type TEXT GENERATED ALWAYS AS (CASE WHEN EXTRACT(ISODOW FROM order_date) BETWEEN 1 AND 5 THEN 'workday' ELSE 'weekend' END) STORED; - 然后在该列上建索引:
CREATE INDEX idx_orders_day_type ON orders(day_type);
SQL Server 中 DATEPART(WEEKDAY, ...) 的坑最多
SQL Server 的 DATEPART(WEEKDAY, ...) 返回值完全依赖 SET DATEFIRST 设置,默认是 7(周日),但很多 DBA 会改成 1(周一)。不显式声明就写 DATEPART(WEEKDAY, d) IN (2,3,4,5,6),换环境就出错。
安全做法是统一用 DATEPART(WEEKDAY, d) + @@DATEFIRST - 1) % 7 归一化到周一=0,或者直接用更稳定的 DATENAME(WEEKDAY, d) 配合字符串匹配(虽然慢一点,但可读性强)。
容易忽略的一点:SQL Server 的 GETDATE() 默认带时区,如果业务横跨多个时区,得确认所有时间字段是否已转为 UTC 再计算 weekday,否则凌晨时段可能被分错组。
需要排除节假日?别硬写 SQL,先建维度表
试图在 SQL 里用一堆 CASE WHEN order_date IN ('2024-01-28', '2024-01-29', ...) 硬编码节假日,维护成本极高,且无法覆盖调休补班日(比如某周六要上班)。
正确路径是单独建一张 calendar_dim 表,至少包含:date、is_workday(布尔)、is_holiday、holiday_name。每天跑个 job 更新未来一年的数据,查询时直接 left join 即可。
这样做的好处:
- 逻辑集中,改一天假期只需 update 一行
- 支持补班日(
is_workday = true但is_holiday = true) - 能复用到其他报表,比如工时统计、排班系统
最常被跳过的一步是:没给 calendar_dim.date 加唯一索引,导致 join 时出现笛卡尔积,count 结果翻倍。记得加 UNIQUE (date)。











