SQL如何筛选早于最近10个工作日(排除周末)的数据
你之前写的datepart(dw, getdate()) > 10跑不出正确结果是必然的:DATEPART(dw)返回的是日期对应的周内序号,不管你怎么设置DATEFIRST,这个值的范围永远是1到7,根本不可能出现大于10的结果,逻辑从根上就错了。
正确实现(SQL Server环境)
核心逻辑是从当前日期往前倒推,数够10个排除周六、周日的工作日,拿到第10个工作日的日期作为筛选边界,所有早于等于这个边界的日期就是你要的历史数据。
以下两种写法可按需选择:
写法1:数值计算法(性能最优)
不需要生成日期序列,纯数学计算,适合大表查询场景,且不受DATEFIRST环境变量影响:
DECLARE @NeedWorkdayNum INT = 10; DECLARE @Boundary DATE; SELECT @Boundary = DATEADD( DAY, - ( @NeedWorkdayNum + ( SELECT COUNT(1) FROM (VALUES(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13),(14)) t(n) WHERE n <= @NeedWorkdayNum + ((@@DATEFIRST + DATEPART(WEEKDAY, GETDATE()) - 2) % 7) AND ((@@DATEFIRST + DATEPART(WEEKDAY, DATEADD(DAY, -n, GETDATE())) - 2) % 7) >= 5 ) ), CAST(GETDATE() AS DATE) ); -- 执行目标查询 SELECT * FROM your_table_name WHERE [date] <= @Boundary;
写法2:递归CTE法(易扩展)
逻辑直观,后续如果要叠加法定节假日排除规则,改造成本非常低:
DECLARE @NeedWorkdayNum INT = 10; DECLARE @Boundary DATE; WITH DateRollback AS ( SELECT CAST(DATEADD(DAY, -1, GETDATE()) AS DATE) AS check_date, CASE WHEN ((@@DATEFIRST + DATEPART(WEEKDAY, DATEADD(DAY, -1, GETDATE())) - 2) % 7) < 5 THEN 1 ELSE 0 END AS is_workday UNION ALL SELECT CAST(DATEADD(DAY, -1, check_date) AS DATE), CASE WHEN ((@@DATEFIRST + DATEPART(WEEKDAY, DATEADD(DAY, -1, check_date)) - 2) % 7) < 5 THEN 1 ELSE 0 END FROM DateRollback WHERE (SELECT SUM(is_workday) FROM DateRollback) < @NeedWorkdayNum ) SELECT TOP 1 @Boundary = check_date FROM DateRollback WHERE is_workday = 1 ORDER BY check_date ASC; -- 执行目标查询 SELECT * FROM your_table_name WHERE [date] <= @Boundary;
注意事项
- 两种写法都做了
DATEFIRST兼容,不管你的环境把周几设为一周第一天,周六、周日的判断都不会出错。 - 如果需要扩展节假日规则,只需要在判断
is_workday的时候,加一个排除节假日表日期的条件即可。 - 你举的示例里当前日期为
'11 Jun 2022'时筛选date <= '27 Jun 2022'存在时间线矛盾(27日晚于11日),属于笔误,以上代码完全匹配你“获取早于最近10个工作日的历史数据”的核心需求。
内容的提问来源于stack exchange,提问作者Baldie47
相关产品推荐
相关产品推荐

