需要一个函数来返回下一个/上一个工作日SQL Server的日期

时间:2014-12-20 16:42:08

标签: sql-server

我需要帮助创建一个sql server(2012)函数,当给出以下变量时

Monday    True/False
Tuesday   True/False
Wednesday True/False
Thursday  True/False
Friday    True/False
Saturday  True/False
Sunday    True/False
Date1     mm/dd/yyyy
Type      Next/Previous

它将返回Date1

的下一个(或上一个)工作日的日期

例如,如果Date1是12/22/2014(星期一),并且传递下面的参数,它将返回2014年12月26日(星期五)

Monday    True
Tuesday   False
Wednesday False
Thursday  False
Friday    True
Saturday  True
Sunday    True
Date1     12/22/2014
Type      Next

4 个答案:

答案 0 :(得分:2)

以下查询计算下一个有效的"日"在一周的任何一天之后。它计算当天的天数。

with days as (
      select 1 as dow, 'Monday' as name, @Monday as flag union all
      select 2, 'Tuesday', @Tuesday union all
      select 3, 'Wednesday', @Wednesday union all
      select 4, 'Thursday', @Thursday union all
      select 5, 'Friday', @Friday union all
      select 6, 'Saturday', @Saturday union all
      select 7, 'Sunday', @Sunday
     )
select d.*, d2.dow as next_dow,
       (case when d2.dow > d.dow then d2.dow - d.dow else d2.dow - d.dow + 7 end) as days_to_next
 from days d cross apply
      (select top 1 d2.dow
       from days d2
       where d2.flag = 'true'
       order by (case when d2.dow > d.dow then 1 else 2 end), d2.dow
      )  d2;

下一步就是查找你想要的日子:

with days as (
      select 1 as dow, 'Monday' as name, @Monday as flag union all
      select 2, 'Tuesday', @Tuesday union all
      select 3, 'Wednesday', @Wednesday union all
      select 4, 'Thursday', @Thursday union all
      select 5, 'Friday', @Friday union all
      select 6, 'Saturday', @Saturday union all
      select 7, 'Sunday', @Sunday
     )
select dateadd(day, 
               (case when d2.dow > d.dow then d2.dow - d.dow else d2.dow - d.dow + 7 end),
               @Date1
              )
 from days d cross apply
      (select top 1 d2.dow
       from days d2
       where d2.flag = 'true'
       order by (case when d2.dow > d.dow then 1 else 2 end), d2.dow
      )  d2
where d.dow = datename(weekday, @Date1);

当然,如果使用非英语国际化设置,datename()可能会返回非英语名称。如果该逻辑不起作用,则可以调整查询。

答案 1 :(得分:1)

您必须先创建table type variable

CREATE TYPE BusinessDateTableType AS TABLE 
( 
   [WeekDay] VARCHAR(50),
   IsBusinessDate BIT 
);

然后创建一个带有上述类型的表值参数的函数:

CREATE FUNCTION UDF_GetNextBusinessDay
(    
    @businessDates BusinessDateTableType READONLY, 
    @type VARCHAR(10),
    @day DATE
)
RETURNS DATE
AS
BEGIN
    -- Declare the return variable here
    DECLARE @nextBusinessDate DATE

    ;WITH cte AS (
        SELECT CASE 
                WHEN @type = 'Next' THEN 1 
                WHEN @type = 'Previous' THEN -1
               END AS i
        UNION ALL
        SELECT CASE 
                WHEN @type = 'Next' THEN i + 1 
                WHEN @type = 'Previous' THEN i -1
               END AS i
        FROM cte
        WHERE ABS(i) < 7
    )                                                                 
    SELECT TOP 1 @nextBusinessDate = DATEADD(day, i, @day)
    FROM cte AS d1
    INNER JOIN @businessDates AS d2 ON DATENAME(DW, DATEADD(day, i, @day)) = d2.WeekDay
    WHERE d2.IsBusinessDate = 1           
    ORDER BY ABS(i)                  

    -- Return the result of the function
    RETURN @nextBusinessDate
END

修改

我们可以使用七个BIT类型变量轻松替换UDF中的表类型变量,然后在UDF中使用table variable并使用这些变量的值填充它:

CREATE FUNCTION UDF_GetNextBusinessDay2
(    
    @IsMonWorkingDay BIT,
    @IsTueWorkingDay BIT,
    @IsWedWorkingDay BIT,
    @IsThuWorkingDay BIT,
    @IsFriWorkingDay BIT,
    @IsSatWorkingDay BIT,
    @IsSunWorkingDay BIT,
    @type VARCHAR(10),
    @day DATE
)
RETURNS DATE
AS
BEGIN
    -- Declare the return variable here
    DECLARE @nextBusinessDate DATE

    DECLARE @businessDates TABLE ([WeekDay] VARCHAR(50), IsBusinessDate BIT) 

    INSERT INTO @businessDates VALUES
    ('Monday', @IsMonWorkingDay),
    ('Tuesday', @IsTueWorkingDay),
    ('Wednesday', @IsWedWorkingDay),
    ('Thursday', @IsThuWorkingDay),
    ('Friday', @IsFriWorkingDay),
    ('Saturday', @IsSatWorkingDay),
    ('Sunday', @IsSunWorkingDay)

    ;WITH cte AS (
        SELECT CASE 
                WHEN @type = 'Next' THEN 1 
                WHEN @type = 'Previous' THEN -1
               END AS i
        UNION ALL
        SELECT CASE 
                WHEN @type = 'Next' THEN i + 1 
                WHEN @type = 'Previous' THEN i -1
               END AS i
        FROM cte
        WHERE ABS(i) < 7
    )                                                                 
    SELECT TOP 1 @nextBusinessDate = DATEADD(day, i, @day)
    FROM cte AS d1
    INNER JOIN @businessDates AS d2 ON DATENAME(DW, DATEADD(day, i, @day)) = d2.WeekDay
    WHERE d2.IsBusinessDate = 1           
    ORDER BY ABS(i)                  

    -- Return the result of the function
    RETURN @nextBusinessDate
END

将UDF的第二个版本与此测试数据一起使用:

DECLARE @type VARCHAR(10) = 'Next'
DECLARE @day DATE = '2014-12-22'
DECLARE @nextBusinessDate DATE

SET @nextBusinessDate = dbo.UDF_GetNextBusinessDay2(1,0,0,0,0,0,1, @type, @day)
SELECT @nextBusinessDate

产生以下结果:

2014-12-28

答案 2 :(得分:1)

此代码通过创建从开始日期开始的三个日期的列表来获取下一个或上一个工作日。如果我们想要上一个工作日,那么如果我们想要下一个工作日我们得到最小的日期,我们就会获得最大的约会。
DECLARE @direction AS INT =1 --If direction is 1 you get the next business day. --If direction is -1 you get the previous business day.
DECLARE @startDate AS DATE ='2017-07-21'
SELECT CASE @direction WHEN 1 THEN MIN(listOfDays.d) ELSE MAX(listOfDays.d) END AS nextBusinessDay FROM( (SELECT DATEADD(DAY,Number*@direction,@startDate)AS d FROM (VALUES (1),(2),(3)) AS Numbers(Number) )) listOfDays WHERE DATEPART(WEEKDAY,listOfDays.d) NOT IN (1,7)

答案 3 :(得分:0)

以下代码从下周工作日的日期列表中选择,不包括周末和非工作日期。

要求:为了使以下代码正常工作,Numbers表必须存在,并且Non_working_dates表必须存在。

Numbers表应至少有一个字段。字段名称必须为Number。该字段不得接受重复。数据类型必须为INT。

Non_working_days表必须至少包含一个字段。字段名称必须为Non_working_date。该字段必须不接受重复值。数据类型必须是没有时间的DATE。

DECLARE @startDate AS DATE = '2017-07-03'
--Step 2. Get the next business day.
 SELECT TOP 1 listOfDates.d AS nextBusinessDay  FROM
   (
   --Step 1. Get a month's worth of dates. 
     The dates must not be weekend days or non working days.
     SELECT DATEADD(DAY,Number,@startDate)AS d FROM Numbers WHERE Number < 30
     ) listOfDates
     WHERE   listOfDates.d> @startDate  AND DATEPART(WEEKDAY,listofdates.d) NOT IN (1,7)
     AND listOfDates.d NOT IN (SELECT Non_working_date FROM Non_working_dates WHERE Non_working_date>=@startDate)
     ORDER BY listOfDates.d ASC