获取每月第n周的第n天的日期

时间:2016-04-22 02:20:50

标签: sql sql-server tsql

我的列中包含“第3周三”,“第2周二”,“每周四”等值。

我想创建一个读取这些字符串的列,并确定该月份是否已经到来,如果有,则返回下个月的日期。如果这个月没有通过,那么它将返回本月的日期。

上述预期结果(2016年4月22日)将是:'05 -18-2016','05 -10-2016','04 -28-2016'。

我希望以数学方式进行,并尽可能避免创建日历表。

感谢。

1 个答案:

答案 0 :(得分:0)

部分答案,绝不是无bug。

这不符合'Every-'条目,但希望能给你一些灵感。我确信有很多测试用例会失败,你最好还是写一个存储过程。

我确实尝试通过计算当月第一天的日期名称和日期编号,然后计算下一个想要的日期并应用偏移量来做到这一点,但它变得很乱。我知道你说没有日期表,但CTE简化了事情。

如何运作

CTE为当前日期和日期名创建日历。一些相当可疑的解析代码从测试数据中提取日期名称并加入CTE。 where子句过滤到大于第N次出现的日期,如果日期已过,则select会增加4周。或者至少那是理论:)

我正在使用DATEFROMPARTS来简化代码,这是一个SQL 2012函数 - 2008年有SO的替代品。

SELECT * INTO #TEST FROM (VALUES ('3rd-Wednesday'), ('2nd-Tuesday'), ('4th-Monday')) A(Value)

SET DATEFIRST 1

;WITH DAYS AS (
    SELECT 
    CAST(DATEADD(MONTH,DATEDIFF(MONTH,0,GETDATE()),N.Number) AS DATE) Date, 
    DATENAME(WEEKDAY, DATEADD(MONTH,DATEDIFF(MONTH,0,GETDATE()),N.Number)) DayName 
    FROM master..spt_values N WHERE N.type = 'P' AND N.number BETWEEN 0 AND 31
)
SELECT 
    T.Value,
    CASE WHEN MIN(D.Date) < GETDATE() THEN DATEADD(WEEK, 4, MIN(D.DATE)) ELSE MIN(D.DATE) END Date
FROM #TEST T
JOIN DAYS D ON REVERSE(SUBSTRING(REVERSE(T.VALUE), 1, CHARINDEX('-', REVERSE(T.VALUE)) -1)) = D.DayName
WHERE D.Date >=
    DATEFROMPARTS(
        YEAR(GETDATE()), 
        MONTH(GETDATE()),
        1+ 7*(CAST(SUBSTRING(T.Value, 1,1) AS INT) -1)
        )
GROUP BY T.Value



Value         Date
------------- ----------
2nd-Tuesday   2016-05-10
3rd-Wednesday 2016-05-18
4th-Monday    2016-04-25