Как получить предыдущий рабочий день за неделю с текущим рабочим днем ​​с использованием SQL-сервера

У меня есть пакет ssis, который работает в рабочие дни (пн-пт). если я получаю файл во вторник, фон (БД), он принимает дату предыдущего рабочего дня и выполняет некоторые транзакции. Если я запускаю задание в пятницу, он должен получить дату понедельника и обработать транзакции.

Я использовал приведенный ниже запрос для получения предыдущей бизнес-даты.

Select Convert(varchar(50), Position_ID) as Position_ID,
       TransAmount_Base,
       Insert_Date as InsertDate
  from tblsample
 Where AsOfdate = Dateadd(dd, -1, Convert(datetime, Convert(varchar(10), '03/28/2012', 101), 120))
Order By Position_ID

Если я выполню этот запрос, я получу результаты вчерашних Transactios. если бы я запустил тот же запрос в понедельник, он должен получить транзакции по пятницам вместо воскресений.

Ответ 1

SELECT  DATEADD(DAY, CASE DATENAME(WEEKDAY, GETDATE()) 
                        WHEN 'Sunday' THEN -2 
                        WHEN 'Monday' THEN -3 
                        ELSE -1 END, DATEDIFF(DAY, 0, GETDATE()))

Я предпочитаю использовать DATENAME для таких вещей более чем через DATEPART, поскольку он устраняет необходимость в настройке DATEFIRST и гарантирует, что изменения в настройках времени и даты на локальных машинах не влияют на результаты. Наконец DATEDIFF(DAY, 0, GETDATE()) удалит временную часть GETDATE(), удалив необходимость конвертировать в varchar (гораздо медленнее).


EDIT (почти 2 года)

Этот ответ был очень ранним в моей карьере SO, и он меня раздражает каждый раз, когда он получает поддержку, потому что я больше не согласен с тем, что я использую DATENAME.

Решением проблемы с маслом будет гораздо больше:

SELECT  DATEADD(DAY, CASE (DATEPART(WEEKDAY, GETDATE()) + @@DATEFIRST) % 7 
                        WHEN 1 THEN -2 
                        WHEN 2 THEN -3 
                        ELSE -1 
                    END, DATEDIFF(DAY, 0, GETDATE()));

Это будет работать для всех языков и настроек DATEFIRST.

Ответ 2

Тогда как насчет:

declare @dt datetime='1 dec 2012'

select case when [email protected]@DATEFIRST=DATEPART(dw,@dt)  
            then DATEADD(d,-2,@dt)  
        when ([email protected]@DATEFIRST)%7=DATEPART(dw,@dt)%7  
            then DATEADD(d,-3,@dt)  
        else DATEADD(d,-1,@dt)  
    end

Ответ 3

Простейшим решением для поиска предыдущего рабочего дня является использование таблицы календаря с столбцом IsBusinessDay или что-то подобное. Ваш запрос выглядит примерно так:

select max(BaseDate)
from dbo.Calendar c
where c.IsBusinessDay = 0x1 and c.BaseDate < @InputDate

Проблема с использованием функций заключается в том, что когда (не если) вы должны создавать исключения по любой причине (национальные праздники и т.д.), код быстро становится недостижимым; с таблицей вы просто UPDATE одно значение. Таблица также упрощает ответы на такие вопросы, как "сколько рабочих дней существует между датами X и Y", которые довольно часто встречаются в задачах отчетности.

Ответ 4

Вы можете легко сделать это вызовом функции, добавив второй параметр, чтобы заменить GetDate() любой желаемой датой. Он будет работать в любой день недели, в любом диапазоне дат, если вы измените GetDate(). Он не изменит дату, если день недели является датой ввода (GetDate())

Declare @DayOfWeek As Integer = 2   -- Monday

Select DateAdd(Day, ((DatePart(dw,GetDate()) + (7 - @DayOfWeek)) * -1) % 7, Convert(Date,GetDate()))

Ответ 5

Эта функция возвращает последний рабочий день и учитывает праздничные и выходные дни. Вам нужно будет создать простой праздничный стол.

-- =============================================
-- Author:      Dale Kilian
-- Create date: 2019-04-29
-- Description: recursive function returns last work day for weekends and 
-- holidays
-- =============================================
ALTER FUNCTION dbo.fnGetWorkWeekday
(
    @theDate DATE
)
RETURNS DATE
AS
BEGIN

DECLARE @importDate DATE = @theDate
DECLARE @returnDate DATE
--Holidays
IF EXISTS(SELECT 1 FROM dbo.Holidays WHERE isDeleted = 0 AND @theDate = Holiday_Date)
BEGIN
SET @importDate = DATEADD(DAY,-1,@theDate);
SET @importDate = (SELECT dbo.fnGetWorkWeekday(@importDate))
END
--Satruday
IF(DATEPART(WEEKDAY,@theDate) = 7)
BEGIN
    SET @importDate = DATEADD(DAY,-1,@theDate);
    SET @importDate = (SELECT dbo.fnGetWorkWeekday(@importDate))
END
--Sunday
IF(DATEPART(WEEKDAY,@theDate) = 1)
BEGIN
    SET @importDate = DATEADD(DAY,-2,@theDate);
    SET @importDate = (SELECT dbo.fnGetWorkWeekday(@importDate))
END


RETURN @importDate;
END
GO

Ответ 6

select  
  dateadd(dd, 
             case DATEPART(dw, getdate()) 
             when 1 
             then -2 
             when 2 
             then -3 
             else -1 
         end, GETDATE())

Ответ 7

спасибо за советы выше, у меня был небольшой вариант в запросе, поскольку мой пользователь нуждался во всех значениях для предыдущей даты. Например, сегодня понедельник, поэтому ему нужно все, начиная с прошлой пятницы в полночь до субботы в полночь. Я сделал это, используя комбо из вышеперечисленного и "между", только если кто-то заинтересован. Я не массивный техник.

-- Declare a variable for the start and end dates.
declare @StartDate as datetime 
declare @EndDate as datetime 

SELECT  @StartDate = DATEADD(DAY, CASE DATENAME(WEEKDAY, GETDATE()) 
WHEN 'Sunday' THEN -2 
WHEN 'Monday' THEN -3 
    ELSE -1 END, DATEDIFF(DAY, 0, GETDATE()))
select @EndDate = @StartDate + 1 
select @StartDate , @EndDate 
-- Later on in the query use "between"
and mydate between @StartDate and @EndDate

Ответ 8

Более элегантный:

select DATEADD(DAY, 
CASE when datepart (dw,Getdate()) < 3 then datepart (dw,Getdate()) * -1 + -1 ELSE -1 END,
cast(GETDATE() as date))