2014-12-20 2 views
-1

мне нужна помощь в создании SQL Server (2012) функцию, когда даны следующие переменныеНужна функция, возвращающая дату следующего/предыдущего рабочего дня SQL Server

 
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 (понедельник), и передавая параметры ниже, он будет возвращать 12/26/2014 (пятница)

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

Не могли бы вы уточнить, чего вы хотите достичь? –

+0

найти следующий или предыдущий бизнес (рабочий день) с учетом базового списка рабочих дней/дней и определенной даты (обычно сегодня) – Bill

ответ

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 

EDIT:

Мы можем легко подставить переменную table-type в UDF с помощью семи переменных типа BIT, а затем использовать table variable внутри UDF и заполнить его значениями этих переменных:

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 
+0

Есть ли способ сделать это, не создавая таблицу. – Bill

+0

@Bill Это не таблица, BusinessDateTableType - это * Тип таблицы *. Это самый элегантный способ передать ** информацию о рабочих днях ** в функцию. –

+0

@ В противном случае вам нужно предоставить * 14 дополнительных переменных * для вашей функции, затем объявить переменную таблицы внутри функции и заполнить переменную таблицы, используя значения 14 дополнительных переменных –

2

Следующий запрос y вычисляет следующий действительный «день» после любого заданного дня недели. И он вычисляет количество дней до этого дня.

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() может вернуться не английские именами, если с неанглийской настройкой интернационализации. Запрос может быть скорректирован, если эта логика не работает.

+0

Спасибо, но можете ли вы помочь мне сделать это функцией? Кроме того, я сделал редактирование, чтобы передать параметр Type для следующего/предыдущего после ответа. – Bill

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)

+0

, пожалуйста, добавьте описание, чтобы ответить Пожалуйста. –

+0

Предполагая, что это обеспечивает желаемый результат, это выглядит намного приятнее, чем принятый ответ. Это случай новых версий с новыми возможностями, которые упростили запись? –

0

Следующий код выбирает из списка дней на следующий рабочий день, за исключением выходных и неработающий даты.

ТРЕБОВАНИЯ: Для работы следующего кода должна существовать таблица Numbers, а также таблица Non_working_dates.

Таблица чисел должна иметь как минимум одно поле. Имя поля должно быть числом. Поле не должно принимать дубликаты. Тип данных должен быть 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>[email protected]) 
    ORDER BY listOfDates.d ASC 
Смежные вопросы