如何在SQL Server(T-SQL)中实现月末最后3个工作日转下月的逻辑?
解决T-SQL中判断当月最后3个工作日并切换月份的需求
这个需求的核心是先识别输入日期是否属于当月最后3个工作日,再决定是否切换到下月的年月格式(YYYYMM)。下面是具体的实现方案,兼容SQL Server 2012及以上版本:
方案1:创建标量函数(可复用)
这个函数可以直接传入日期,返回目标年月,适合在多个查询中重复调用:
CREATE FUNCTION dbo.GetTargetMonth (@InputDate DATE) RETURNS VARCHAR(6) AS BEGIN -- 显式设置周日为一周第一天,避免环境DATEFIRST差异影响判断 SET DATEFIRST 7; DECLARE @LastDayOfMonth DATE = EOMONTH(@InputDate); -- 计算当月最后1个工作日 DECLARE @LastWorkDay1 DATE = CASE DATEPART(WEEKDAY, @LastDayOfMonth) WHEN 1 THEN DATEADD(DAY, -2, @LastDayOfMonth) -- 最后一天是周日,往前推2天到周五 WHEN 7 THEN DATEADD(DAY, -1, @LastDayOfMonth) -- 最后一天是周六,往前推1天到周五 ELSE @LastDayOfMonth -- 最后一天是工作日,直接用 END; -- 计算当月倒数第2个工作日 DECLARE @LastWorkDay2 DATE = CASE DATEPART(WEEKDAY, DATEADD(DAY, -1, @LastWorkDay1)) WHEN 1 THEN DATEADD(DAY, -2, DATEADD(DAY, -1, @LastWorkDay1)) WHEN 7 THEN DATEADD(DAY, -1, DATEADD(DAY, -1, @LastWorkDay1)) ELSE DATEADD(DAY, -1, @LastWorkDay1) END; -- 计算当月倒数第3个工作日 DECLARE @LastWorkDay3 DATE = CASE DATEPART(WEEKDAY, DATEADD(DAY, -1, @LastWorkDay2)) WHEN 1 THEN DATEADD(DAY, -2, DATEADD(DAY, -1, @LastWorkDay2)) WHEN 7 THEN DATEADD(DAY, -1, DATEADD(DAY, -1, @LastWorkDay2)) ELSE DATEADD(DAY, -1, @LastWorkDay2) END; -- 判断输入日期是否属于最后3个工作日 DECLARE @IsLastThreeWorkDays BIT = CASE WHEN @InputDate BETWEEN @LastWorkDay3 AND @LastWorkDay1 THEN 1 ELSE 0 END; -- 返回目标年月:满足条件则返回下月,否则返回当月 RETURN CASE @IsLastThreeWorkDays WHEN 1 THEN FORMAT(DATEADD(MONTH, 1, @InputDate), 'yyyyMM') ELSE FORMAT(@InputDate, 'yyyyMM') END; END;
测试你的示例
执行以下查询验证结果:
SELECT dbo.GetTargetMonth('20180627') AS TargetMonth; -- 输出:201807 SELECT dbo.GetTargetMonth('20180529') AS TargetMonth; -- 输出:201806 SELECT dbo.GetTargetMonth('20190226') AS TargetMonth; -- 输出:201903 SELECT dbo.GetTargetMonth('20181227') AS TargetMonth; -- 输出:201901
方案2:单查询实现(无需创建函数)
如果只是一次性使用,可以用CTE(公共表表达式)把逻辑整合到单个查询中:
DECLARE @InputDate DATE = '20180627'; SET DATEFIRST 7; -- 统一日期判断规则 WITH LastWorkDays AS ( SELECT EOMONTH(@InputDate) AS LastDayOfMonth, CASE DATEPART(WEEKDAY, EOMONTH(@InputDate)) WHEN 1 THEN DATEADD(DAY, -2, EOMONTH(@InputDate)) WHEN 7 THEN DATEADD(DAY, -1, EOMONTH(@InputDate)) ELSE EOMONTH(@InputDate) END AS LastWorkDay1 ), LastWorkDays2 AS ( SELECT LastDayOfMonth, LastWorkDay1, CASE DATEPART(WEEKDAY, DATEADD(DAY, -1, LastWorkDay1)) WHEN 1 THEN DATEADD(DAY, -2, DATEADD(DAY, -1, LastWorkDay1)) WHEN 7 THEN DATEADD(DAY, -1, DATEADD(DAY, -1, LastWorkDay1)) ELSE DATEADD(DAY, -1, LastWorkDay1) END AS LastWorkDay2 ), LastWorkDays3 AS ( SELECT LastDayOfMonth, LastWorkDay1, LastWorkDay2, CASE DATEPART(WEEKDAY, DATEADD(DAY, -1, LastWorkDay2)) WHEN 1 THEN DATEADD(DAY, -2, DATEADD(DAY, -1, LastWorkDay2)) WHEN 7 THEN DATEADD(DAY, -1, DATEADD(DAY, -1, LastWorkDay2)) ELSE DATEADD(DAY, -1, LastWorkDay2) END AS LastWorkDay3 ) SELECT CASE WHEN @InputDate BETWEEN LastWorkDay3 AND LastWorkDay1 THEN FORMAT(DATEADD(MONTH, 1, @InputDate), 'yyyyMM') ELSE FORMAT(@InputDate, 'yyyyMM') END AS TargetMonth FROM LastWorkDays3;
注意事项
- 节假日处理:以上逻辑只排除了周六和周日,如果需要考虑法定节假日,你需要额外维护一个节假日表,在判断工作日时加入对该表的查询。
- SQL Server版本:
EOMONTH()和FORMAT()函数从SQL Server 2012开始支持,如果是更早版本,需要用DATEADD(DAY, -DAY(@InputDate), DATEADD(MONTH, 1, @InputDate))替代EOMONTH(),用CONVERT(VARCHAR(6), @Date, 112)替代FORMAT()来生成YYYYMM格式。
内容的提问来源于stack exchange,提问作者Rob
相关产品推荐
相关产品推荐

