2014-01-10 4 views
0

Как бы вычислить последний день недели текущего месяца с датой с использованием SQL?Как найти последний будний день текущего месяца с помощью SQL

Я смог получить последний день текущего месяца, но не знаю, как сделать последний рабочий день программным путем.

Я не хочу генерировать таблицу поиска в календаре.

Вот последний день месяца кода я в настоящее время с помощью:

declare @date datetime 
set @date='1/4/13' 
select DATEADD(d, -1, DATEADD(m, DATEDIFF(m, 0, @date) + 1, 0)) 
+0

это забавно, что верхняя родственный http://stackoverflow.com/questions/60174/how-can- i-prevent-sql-injection-in-php? rq = 1 –

ответ

2

Я знаю, что это не самый интуитивный или эффективный или простой способ сделать это. Но вот мое решение, чтобы найти последнюю Weekday месяца ...

declare @date datetime, @lastDate datetime, @lastWeekDay datetime 
set @date='05/4/2014';--'1/1/2014' 
set @lastDate = (SELECT DATEADD(d, -1, DATEADD(m, DATEDIFF(m, 0, @date) + 1, 0))); 
/* @dayOfWeek represents -- 0-Monday through 7-Sunday */ 
declare @dayOfWeek INT = (SELECT DATEDIFF(dd, 0, @lastDate) % 7); 
/* If last date is sat/sun substract 1 or 2 days from last date */ 
set @lastWeekDay = (SELECT CASE WHEN @dayOfWeek = 5 THEN DATEADD(dd, -1, @lastDate) 
           WHEN @dayOfWeek = 6 THEN DATEADD(dd, -2, @lastDate) 
           ELSE @lastDate END) 
SELECT @lastWeekDay; 
0

вы можете попробовать использовать функцию день недели, как

select datename(dw, getdate()) 

, а затем оттуда использовать, что в запросе на выведите последний день недели в месяце

, чтобы вы могли получить что-то вроде этого, фактически не создав таблицу «даты»

declare @date datetime 
set @date='3/4/13' 
select case when datename(dw, DATEADD(d, -1, DATEADD(m, DATEDIFF(m, 0, @date) + 1, 0))) = 'Saturday' 
      then DATEADD(d, -2, DATEADD(m, DATEDIFF(m, 0, @date) + 1, 0)) 
      when datename(dw, DATEADD(d, -1, DATEADD(m, DATEDIFF(m, 0, @date) + 1, 0))) = 'Sunday' 
      then DATEADD(d, -3, DATEADD(m, DATEDIFF(m, 0, @date) + 1, 0)) 
      else DATEADD(d, -1, DATEADD(m, DATEDIFF(m, 0, @date) + 1, 0)) 
      end 
, case when datename(dw, DATEADD(d, -1, DATEADD(m, DATEDIFF(m, 0, @date) + 1, 0))) = 'Saturday' 
      then datename(dw, DATEADD(d, -2, DATEADD(m, DATEDIFF(m, 0, @date) + 1, 0))) 
      when datename(dw, DATEADD(d, -1, DATEADD(m, DATEDIFF(m, 0, @date) + 1, 0))) = 'Sunday' 
      then datename(dw, DATEADD(d, -3, DATEADD(m, DATEDIFF(m, 0, @date) + 1, 0))) 
      else datename(dw, DATEADD(d, -1, DATEADD(m, DATEDIFF(m, 0, @date) + 1, 0))) 
      end 
+1

Любая конкретная причина, по которой вы не выбрали 'datepart (dw, @date)' вместо того, который возвращает строку? –

+0

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

+0

Будет ли строка быть только на английском? –

1

Это один гораздо сложнее, чем это должно быть. Я сделал аналогичную работу, используя datename, но только потому, что я знаю, что все системы, которые я использую, будут настроены на английском языке как язык по умолчанию. Без этого предположения вы должны использовать datepart(dw, ..., и вам также нужно беспокоиться о SET DATEFIRST. Я также предполагаю, что «будний день» означает «Mon, Tue, Wed, Thu, Fri» и исключая субботу и воскресенье. (Или же TFIG применяется по всему миру?)

Таким образом, прогуливаясь по моей методике, мы начнем с того, что у вас было:

DECLARE 
    @Date datetime 
,@BOM datetime 

--SET @Date = '1/4/14' -- Should return Jan 31, 2013 
SET @Date = '5/4/14' -- Should return May 30, 2014 (not May 31) 
SET @BOM = DATEADD(m, DATEDIFF(m, 0, @date) + 1, 0) 

PRINT @BOM 

Это устанавливает @BOM (начало месяца) в первый день месяц после того, что у вас есть в @Date. (Я разделил это на несколько операторов, потому что в противном случае вам придется повторять эту функцию по всему месту в коде ниже).

Далее вам нужно «вернуться» с этого дня на один, два или три дня , Если BOM - понедельник, -3 - до прошлой пятницы; если BOM - воскресенье, -2 - до пятницы; в противном случае -1 высадит вас в будний день. Основываясь на значениях, возвращаемых datepart, нет никакого алгоритма, который я могу придумать, чтобы сгенерировать этот -1/-2/-3-распространение, поэтому я использую оператор case (я отказываюсь от процедур циклизации из-за слишком коротких ошибок для работы с базой данных).

PRINT datepart(dw, @BOM) -- To see what is is 

PRINT dateadd(dd, case 
        when datepart(dw, @BOM) = 2 then -3 
        when datepart(dw, @BOM) = 1 then -2 
        else -1 
        end 
        ,@BOM) 

Увы, это работает, только если ваш экземпляр SQL настроен с настройкой «первый день недели» по умолчанию; это проверяется через PRINT @@datefirst, где 7 (по умолчанию) = Солнце, 6 = Сб и т. д. Опять же, ни один из возможных алгоритмов не предлагает себя, а деловая ситуация превращается в беспорядок:

PRINT dateadd(dd, case 
        when @@datefirst = 7 and datepart(dw, @BOM) = 2 then -3 
        when @@datefirst = 7 and datepart(dw, @BOM) = 1 then -2 
        when @@datefirst = 6 and datepart(dw, @BOM) = 3 then -3 
        when @@datefirst = 6 and datepart(dw, @BOM) = 2 then -2 
        when @@datefirst = 5 and datepart(dw, @BOM) = 4 then -3 
        when @@datefirst = 5 and datepart(dw, @BOM) = 3 then -2 
        when @@datefirst = 4 and datepart(dw, @BOM) = 5 then -3 
        when @@datefirst = 4 and datepart(dw, @BOM) = 4 then -2 
        when @@datefirst = 3 and datepart(dw, @BOM) = 6 then -3 
        when @@datefirst = 3 and datepart(dw, @BOM) = 5 then -2 
        when @@datefirst = 2 and datepart(dw, @BOM) = 7 then -3 
        when @@datefirst = 2 and datepart(dw, @BOM) = 6 then -2 
        when @@datefirst = 1 and datepart(dw, @BOM) = 1 then -3 
        when @@datefirst = 1 and datepart(dw, @BOM) = 7 then -2 
        else -1 
        end 
        ,@BOM) 

Уродливое, что ли? И для этого нужно учитывать и петлевые структуры.Конечно, если вы можете рассчитывать на всегда иметь один и тот же язык, на ваших экземпляров SQL, это то, что гораздо проще:

PRINT dateadd(dd, case 
        when datename(dw, @BOM) = 'Monday' then -3 
        when datename(dw, @BOM) = 'Sunday' then -2 
        else -1 
        end 
        ,@BOM) 

Любые или все вышеперечисленное может и должно быть «сцеплены вниз» в одном заявлении или запросе (или, лучше, функция); если вы можете смело делать предположения относительно языка установки и/или первого дня недели, вы можете сократить его еще больше.

+0

Мне нравятся объяснения и усилия, которые вошли в этот. Спасибо за это. Я думаю, что менее подробный запрос с использованием Day of Week # от пользователя2989408 проще в моих целях. – FistOfFury

2

Я знаю, что это старый, но я искал однострочный и приземлился здесь. Вот тот, который я выяснил:

set DATEFIRST 1 

declare @testDate datetime 
select @testDate = '8/1/2016' 

select dateadd(day, -(select max(dayCount) from (values (DATEPART(WEEKDAY, EOMONTH(@testDate)) - 5), (0)) as allDays(dayCount)), EOMONTH(@testDate)) 

Это будет работать только с SQL Server 2012 и выше.

Как это работает:

  1. Получить последнюю календарную дату для даты тестирования
  2. Получить день-недели, где понедельник является 1, суббота 6 и воскресенье является 7
  3. Вычтите день недели, а затем установите значение 0, если оно отрицательное, практически максимальное (0, день недели - 5). Это оставит 1 для субботы или 2 для воскресенья.
  4. Вычесть дни (1, если в субботу, 2, если в воскресенье) с последнего дня месяца
+0

это выглядит довольно близко. я думаю, если вы включите @@ DATEFIRST, вы практически там. – FistOfFury

Смежные вопросы