【问题标题】:What is the best way to find next n week days找到下 n 个工作日的最佳方法是什么
【发布时间】:2015-01-09 04:52:55
【问题描述】:

我从我提出的以下问题中得到以下代码: Passing in Week Day name to get nearest date in SQL

我需要根据今天的日期在我的表中找到对应的星期几的下 4 个工作日,即,如果今天是2015-01-24,则结果应该是1/24, 1/31, 2/7, 2/14 对应的Saturdays

表格

示例查询

create table #t
(
    jobId int,
    personId int,
    frequencyVal varchar(10)
);

insert into #t values (1,100,'Mondays'),(2,101,'Saturdays');

WITH cte(n) AS
(
    SELECT 0
    UNION ALL
    SELECT n+1 FROM cte WHERE n < 3
)

select #t.jobId, #t.personId, #t.frequencyVal, STUFF(a.d, 1, 1, '') AS FutureDates
from #t
cross apply (SELECT CASE #t.frequencyVal
                         WHEN 'SUNDAYS'    THEN 1 
                         WHEN 'MONDAYS'    THEN 2 
                         WHEN 'TUESDAYS'   THEN 3 
                         WHEN 'WEDNESDAYS' THEN 4 
                         WHEN 'THURSDAYS'  THEN 5 
                         WHEN 'FRIDAYS'    THEN 6
                         WHEN 'SATURDAYS'  THEN 7 
                    END)tranlationWeekdays(n)
cross apply (select ',' +  CONVERT(varchar(10),  CONVERT(date,dateadd(WEEK, cte.n,CONVERT(DATE, DATEADD(DAY, (DATEPART(WEEKDAY, GETDATE()) + tranlationWeekdays.n) % 7, GETDATE()))))) from cte FOR XML PATH('')) a(d);

drop table #t;

预期结果

【问题讨论】:

  • 如果星期一作为参数传递,你想得到下一个 4 星期一而不考虑月份吗? @webdad3

标签: sql sql-server


【解决方案1】:

获取当月的第一天。

DECLARE @FIRSTDAY DATE=DATEADD(month, DATEDIFF(month, 0, GETDATE()), 0)

创建表并插入值

create table #t
(
    jobId int,
    personId int,
    frequencyVal varchar(10)
);

insert into #t values (1,100,'Mondays'),(2,101,'Saturdays');

您可以根据自己的情况使用以下任一查询。

QUERY 1 :选择当前月份的前 4 周作为特定工作日

 -- Gets the first day of current month
 DECLARE @FIRSTDAY DATE=DATEADD(month, DATEDIFF(month, 0, GETDATE()), 0)

;WITH  CTE as
(
     -- Will find all dates in current month
     SELECT @FIRSTDAY as DATES
     UNION ALL
     SELECT DATEADD(DAY,1,DATES)    
     FROM    CTE
     WHERE   DATES < DATEADD(MONTH,1,@FIRSTDAY)
 )
,CTE2 AS
(
   -- Join the #t table with  CTE on the datename+'s' 
   SELECT jobId,personId,frequencyVal, DATES,
   ROW_NUMBER() OVER(PARTITION BY DATENAME(WEEKDAY,CTE.DATES) ORDER BY CTE.DATES) DATECNT
   FROM CTE
   JOIN #t ON DATENAME(WEEKDAY,CTE.DATES)+'s' = #t.frequencyVal
   WHERE MONTH(DATES)= MONTH(GETDATE())   
)
-- Converts to CSV and make sure that only 4 days are generated for month
SELECT  DISTINCT C2.jobId,C2.personId,frequencyVal,   
        SUBSTRING(
        (SELECT ', ' + CAST(DATEPART(MONTH,DATES) AS VARCHAR(2)) + '/'  + 
                       CAST(DATEPART(DAY,DATES) AS VARCHAR(2))
        FROM CTE2 
        WHERE C2.jobId=jobId AND C2.personId=personId AND DATECNT<5
        ORDER BY CTE2.DATES
        FOR XML PATH('')),2,200000) futureDates
        FROM CTE2 C2

例如,在Query1中最近的日期(这里我们以星期六为例)

  • 2015-Jan-10 将是 01/03,01/10,01/17,01/24
  • 2015-Jan-24 将是 01/03,01/10,01/17,01/24

QUERY 2 : 为特定的工作日选择当前月份中最近 4 周的日子

-- Gets the first day in current month
DECLARE @FIRSTDAY DATE=DATEADD(month, DATEDIFF(month, 0, GETDATE()), 0)

;WITH  CTE as
(
     -- Will find all dates in current
     SELECT CAST(@FIRSTDAY AS DATE) as DATES
     UNION ALL
     SELECT DATEADD(DAY,1,DATES)    
     FROM    CTE
     WHERE   DATES < DATEADD(MONTH,1,@FIRSTDAY)
 )
,CTE2 AS
(
   -- Join the #t table with  CTE on the datename+'s' 
   SELECT jobId,personId,frequencyVal,DATES,
   -- Get week difference for each weekday        
   DATEDIFF(WEEK,DATES,GETDATE()) WEEKDIFF,
   -- Count the number of weekdays in a month
   COUNT(DATES) OVER(PARTITION BY DATENAME(WEEKDAY,CTE.DATES)) WEEKCOUNT
   FROM CTE
   JOIN #t ON DATENAME(WEEKDAY,CTE.DATES)+'s' = #t.frequencyVal 
   WHERE MONTH(DATES)= MONTH(GETDATE())   
)
-- Converts to CSV and make sure that only nearest 4 week of days are generated for month
SELECT  DISTINCT C2.jobId,C2.personId,frequencyVal,
         SUBSTRING(
        (SELECT ', ' + CAST(DATEPART(MONTH,DATES) AS VARCHAR(2)) + '/'  + 
                       CAST(DATEPART(DAY,DATES) AS VARCHAR(2))
        FROM CTE2 
        WHERE C2.jobId=jobId AND C2.personId=personId AND C2.frequencyVal=frequencyVal AND
                       ((WEEKDIFF<3 AND WEEKDIFF>-3 AND WEEKCOUNT = 5) OR WEEKCOUNT <= 4)
        ORDER BY CTE2.DATES
        FOR XML PATH('')),2,200000) futureDates
FROM CTE2 C2

例如,在Query2中最近的日期(这里我们以星期六为例)

  • 2015-Jan-10 将是 01/03,01/10,01/17,01/24
  • 2015-Jan-24 将是 01/10,01/17,01/24,01/31

问题 3:选择与月份无关的特定工作日的下 4 周日期

;WITH  CTE as
(
     -- Will find all dates in current month
     SELECT CAST(GETDATE() AS DATE) as DATES
     UNION ALL
     SELECT DATEADD(DAY,1,DATES)    
     FROM    CTE
     WHERE   DATES < DATEADD(DAY,28,GETDATE())
 )
,CTE2 AS
(
   -- Join the #t table with  CTE on the datename+'s' 
   SELECT jobId,personId,frequencyVal, DATES,
   ROW_NUMBER() OVER(PARTITION BY DATENAME(WEEKDAY,CTE.DATES) ORDER BY CTE.DATES) DATECNT
   FROM CTE
   JOIN #t ON DATENAME(WEEKDAY,CTE.DATES)+'s' = #t.frequencyVal  
)
-- Converts to CSV and make sure that only 4 days are generated for month
SELECT  DISTINCT C2.jobId,C2.personId,frequencyVal,   
        SUBSTRING(
        (SELECT ', ' + CAST(DATEPART(MONTH,DATES) AS VARCHAR(2)) + '/'  + 
                       CAST(DATEPART(DAY,DATES) AS VARCHAR(2))
        FROM CTE2 
        WHERE C2.jobId=jobId AND C2.personId=personId AND C2.frequencyVal=frequencyVal 
              AND DATECNT < 5
        ORDER BY CTE2.DATES
        FOR XML PATH('')),2,200000) futureDates
        FROM CTE2 C2

如果GETDATE()(如果是星期六),则输出如下

2015-01-05 - 1/10, 1/17, 1/24, 1/31
2015-01-24 - 1/24, 1/31, 2/7, 2/14

【讨论】:

  • 这似乎有效。但我有几个问题。随着我接近二月,它会给我下个月的日期吗?我也发现了一个问题,当我向 #t (3,102,'Sundays') 添加另一行时,它只返回了 3 个日期。这都是 Query2 的想法?
  • Query1 似乎像宣传的那样工作(即使在#t 中有新的星期日行。所以我在我的机器上更改了我的日期,它只显示了当月的未来日期。无论如何有它进入下个月?
  • Query2 现在也可以工作了。我已经更新了。请检查@webdad3
  • 是的 Query2 也可以。我仍然只看到当月的结果。我也许可以使用它,但如果我能将未来的日期放在当前月份和未来月份,那就太酷了。
  • 您在类似问题中提到“因此,如果我尝试在下周二运行此查询,jobId:1 的未来日期将删除 1/5 并添加 2/2”。所以我认为你只需要本月的结果。没有问题。我会为你做。您能否简要介绍一下您需要在结果中应用的标准? @webdad3
【解决方案2】:

我认为这是一种更简单的方式,我认为它符合您的要求 请注意,我已将您的 frequency_val 列更改为从 SQL 服务器角度表示星期几的整数,并添加了一个计算列来说明如何轻松地从中得出日期名称。

/*************************************************/
--Set up our sample table
/*************************************************/
declare @t table
(
    jobId int,
    personId int,
    --frequencyVal varchar(10) -- why store a string when a tiny int will do.
    frequency_val tinyint,
    frequency_day as datename(weekday,frequency_val -1) + 's'
)

insert into @t values (1,100,1),--'Mondays'), (2,101,6),--'Saturdays'); (3,101,7),--'Sundays'); (4,100,2)--'Tuesdays'), --select * from @t

/*************************************************/ --Declare & initialise variables /*************************************************/ declare @num_occurances int = 4 declare @from_date date = dateadd(dd,3,getdate()) -- this will allow you to play with the date simply by changing the increment value

/*************************************************/ -- To get a row for each occurance /*************************************************/ ;with r_cte (days_ahead, occurance_date) as (select 0, convert(date,@from_date,121) union all select r_cte.days_ahead +1, convert(date,dateadd(DD, r_cte.days_ahead+1, @from_date),121) from r_cte where r_cte.days_ahead < (7 * @num_occurances) -1 ) select t.*, r_cte.occurance_date from @t t inner join r_cte on DATEPART(WEEKDAY, dateadd(dd,@@DATEFIRST - 1 ,r_cte.occurance_date)) = t.frequency_val

/*************************************************/ --To get a single row with a CSV of every occurance /*************************************************/ ;with r_cte (days_ahead, occurance_date) as (select 0, convert(date,@from_date,121) union all select r_cte.days_ahead +1, convert(date,dateadd(DD, r_cte.days_ahead+1, @from_date),121) from r_cte where r_cte.days_ahead < (7 * @num_occurances) -1 ) select t.*, STUFF( (select ', ' + convert(varchar(2),datepart(month,occurance_date),0) + '/' + convert(varchar(2),datepart(day,occurance_date),0) as occurance from r_cte where DATEPART(WEEKDAY, dateadd(dd,@@DATEFIRST - 1 ,r_cte.occurance_date)) = t.frequency_val FOR XML PATH (''),TYPE).value('.','varchar(30)') ,1,2,'') occurance_date -- rest of STUFF() function from @t t

【讨论】:

  • @GB 您当前设置的方式不起作用。当我运行它时,星期一被设置为 3 天。当我将 dateadd(dd,3,getdate()) 更改为 0 时效果更好,但是当我将星期日添加到组合中时,它又关闭了。
  • @webdad3 - 现在应该工作得更好。我知道您将在最终版本中使用 GetDate() 的参考日期,但这样您就可以针对不同的输入对其进行测试,我已经离开 dateadd(dd,3,getdate()) 以说明它应该在明天或其他任何一天仍然有效。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-10-13
  • 1970-01-01
  • 2010-09-23
  • 1970-01-01
  • 2019-06-20
  • 2022-09-27
  • 1970-01-01
相关资源
最近更新 更多