Цкль ​​день недели - PullRequest
       52

Цкль ​​день недели

5 голосов
/ 19 июля 2011

Меня попросили принять участие в конкурсе T-SQL, мне нужно получить эквивалентный день в следующем месяце на основе позиции дня, например:

Сегодня 18/07/2011, другими словами, этотретий понедельник этого месяца ...

Теперь мне нужно получить третий понедельник следующего месяца (15/08/2011) для запроса SQL Server.

Это похоже на повторение календаря Googleправила.

Я пробовал много формул, но это становится очень сложным, любая помощь будет очень признательна.

Ответы [ 7 ]

3 голосов
/ 19 июля 2011

Этот синтаксис вычислит тот же день на следующей неделе и примет нулевое значение, если его не существует

declare @t datetime

set @t = '2008-01-29'

select case (datepart(day, @t)+ 6) / 7
           when (datepart(day, @t + 28)+ 6) / 7 then @t + 28
           when (datepart(day, @t + 35)+ 6) / 7 then @t + 35
           else null end
2 голосов
/ 19 июля 2011
CREATE FUNCTION fnSameDayOfWeekNextMonth (@Date datetime)
RETURNS datetime
AS BEGIN
  RETURN (
    SELECT
      DATEADD(WEEK,
              CASE (MONTH(ApproxDate) - MONTH(@Date) + 12) % 12
                WHEN 2 THEN -1
                ELSE ThisDayNum - (DAY(ApproxDate) - 1) / 7
              END,
              ApproxDate)
    FROM (
      SELECT
        ThisDayNum = (DAY(@Date) - 1) / 7,
        ApproxDate = DATEADD(WEEK, 5, @Date)
    ) s
  )
END

Эта функция реализует следующую логику:

  1. Получение приблизительной даты в качестве заданной даты плюс ровно 5 недель.

  2. Получить номер дня недели в месяце данной даты.

  3. Если месяц приблизительной даты составляет 2 месяца после месяца данной даты, вычтите одну неделю из приблизительной датыи верните результат.

  4. В противном случае получите то же самое, что и # 2, но для приблизительной даты (вместо заданной даты).

  5. Получите разницу между # 2 и # 4.

  6. Вычтите # 5, как количество недель, из приблизительной даты и верните результат.

Шаг № 3 означает, что если текущая дата является пятой, то итоговая дата будет четвертым днем ​​недели следующего месяца, потому что пятая будет невозможна. Обновление: @t-clausen.dk предложило, на мой взгляд, лучшую идею возврата NULL в таких случаях.Вышеуказанная функция может быть легко изменена в соответствии с тем же соглашением: часть …WHEN 2 THEN -1… должна быть просто изменена на …WHEN 2 THEN NULL….

1 голос
/ 19 июля 2011

Я бы использовал для этого таблицу календаря: вы можете заполнить ее одной строкой в ​​день, а затем вам нужно будет написать простой запрос, чтобы ответить на ваш вопрос.Вы по-прежнему можете использовать функции и логику, предложенные другими авторами, для заполнения таблицы, но это будет разовая задача.Если вы много работаете с повторяющимися датами, у вас может быть столбец со строковыми значениями «первый понедельник», «третья среда» и т. Д. Для упрощения запросов.

Ваш запрос может выглядеть примерно так:

select 
    min(BaseDate)
from 
    dbo.Calendar
where 
    DayDescription = (select DayDescription from dbo.Calendar where BaseDate = @Today) and
    BaseDate > @Today

Другими словами, дайте мне первую дату после сегодняшнего дня, когда описание дня (т. Е. «Третий понедельник» или что-то еще) совпадает с сегодняшним днем.Эта дата должна быть третий понедельник следующего месяца.Точный запрос будет зависеть от ваших типов данных и значений параметров поиска.

Как общее наблюдение, на многие (большинство?) Запросов, связанных с датами, легче ответить, используя предварительно заполненную таблицу, чем функцию.

1 голос
/ 19 июля 2011

К счастью для вас, мне пришлось сделать это один раз: http://blog.prokrams.com/2007/06/18/determining-the-ordinal-of-a-weekday/

Create Function fn_ReturnOrdinalDay( @TestDate datetime) returns int
begin

 declare@ordinal int,@loopdate datetime

set @loopdate = @TestDateset 
set @ordinal = 0

while datepart(m, @TestDate) = datepart(m, @loopdate)
  begin
            set @ordinal = @ordinal + 1
            set @testdate = dateadd(d, -7, @testdate)
  end

return @ordinal
end

ETA: чтобы найти следующий день того же порядкового номера, вы просто добавляете +7 к сегодняшней дате доКонечно, fn_ReturnOrdinalDay вернул тот же результат, что и сегодня.

0 голосов
/ 04 августа 2011

- за 3-ю среду

DATEPART (DW, PromptDate) + DATEPART (DW, PromptDate - 7) + DATEPART (DW, PromptDate - 14) = 12

- Это не должна быть четвертая или пятая среда, поскольку нас интересует только третья среда.

И DATEDIFF (МЕСЯЦ, PromptDate - 14, PromptDate - 21) <> 0

0 голосов
/ 27 июля 2011

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

 CREATE FUNCTION [dbo].[fncSameDayOfWeekAnotherMonth] (@Date datetime, @Months int)
RETURNS datetime
AS BEGIN
  declare @FONM datetime,  @day TinyInt, @ordinal tinyint, @FDM datetime, @result datetime

  Set @FONM = DATEADD(dd,-(DAY(DATEADD(mm,@Months,@Date))-1),DATEADD(mm,@Months,@Date))--first day of month
  Set @day = datepart(weekday,@Date)+7
  set @ordinal=ceiling( cast(datepart(day,@Date)as float)/7 )--ordinal day
  set @FDM = DateAdd(day, ((@day -DatePart(weekday, @FONM))%7), @FONM)--first day of week (monday, friday, etc) on month

  set @result = DateAdd(day, (@ordinal-1)*7, @FDM)

    RETURN case when DATEDIFF(month, @Date, @result) >  @Months  then DateAdd(day, (@ordinal-2)*7, @FDM) else @result end --if result exceeds number of months just subtract one week from ordinal

END

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

Я знаю, что в нем много кода и его можно улучшить, но, по крайней мере, он работает довольно хорошо.

0 голосов
/ 19 июля 2011

Если от вас требуется только выполнить задание, указанное выше, то это относительно просто.

  1. выясните название дня, используя функцию формата даты (я не могу вспомнить точное название).

  2. Выполните цикл с начала месяца и ведите подсчет числа, например, вторников, с которыми вы столкнулись, прежде чем дойдете до даты ввода.

    Затем выполните цикл следующего месяца (или любого другого месяца) x количество раз, как только вы достигнете нужного числа, возьмите следующий вторник

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...