Pull to refresh

SQL Server — Узнать неделю месяца из недели года

Reading time2 min
Views5.5K
Заинтересовался вопросом, вот если дана дата, то как узнать к какой неделе месяца она принадлежит? Чтобы знать, к примеру, что 7.05.2009 это вторая неделя мая, а 15.06.2009 третья неделя июня. В SQL Server есть встроенная функция, позволяющая определить, к какой неделе года (от 1 до 53) относится заданная дата. Может это поможет? Вот например можно узнать на какой неделе закончился предыдущий месяц и вычесть из текущей. Например, прошлый месяц закончился на 22 неделе, а новый начался 23, следующая неделя 24, потом 25… Отнимем 22 из каждой недели текущего месяца и получим соответственно 1, 2, 3… неделю.

Неделя_месяца = текущая_неделя_года - последняя_неделя_пм

Это все было бы хорошо, если бы не одно но. Новый месяц почти всегда начинается посередине недели. И если прошлый месяц закончился на 22-ой неделе, то текущий месяц имеет неплохие шансы начаться тоже с 22-ой недели. Если начнем вычитать то получим месяцы начиная с нулевого (22 — 22), 0,1,2… непорядок. Надо бы, как нибудь сделать, чтобы если неделя на которую закончился старый месяц и начался новый совпадают, то к числу недель прибавить единичку. Воспользуемся возможностями деления. Если делить целые (int), то результат деления тоже будет целым числом, а дробная часть будет отброшена. Таким образом

Неделя_месяца = текущая_неделя_года + (последняя_неделя_пм/первая_неделя_тм - последняя_неделя_пм)

Теперь нумерация недель месяца будет начинаться правильно, с единицы, а не с нуля. Всё? Ещё нет. Мы упустили из виду первый месяц года, то есть январь. Если мы из его первой недели станем вычитать последнюю неделю, предыдущего месяца, то есть последнюю неделю декабря прошлого года, то -52 (1 — 53) это не совсем то что нам нужно. Обычно чтобы избавиться от неугодного числа его умножат на ноль. Поступим так же. Сделаем так что если месяц первый то число которое нужно отнять от его недель умножается на 0, а если месяц не первый, то на 1. Интересно, как можно превратить единицу в ноль, а все остальные числа, которое больше единицы, в единицу. Здесь нам тоже поможет деление целых чисел, в придачу с возведением в степень. Вот так:

2 - 21/X

Теперь уравнение по нахождению недели месяца будет выглядеть так:

Неделя_месяца = текущая_неделя_года + (последняя_неделя_пм/первая_неделя_тм - последняя_неделя_пм)х(2 - 21/номер_месяца)

Переведем все это на язык Transact SQL

Номер текущей недели: DATEPART(wk, @dt)
Номер первой недели текущего месяца: DATEPART(wk, DATEADD(MONTH, MONTH(@dt)-1, convert(date,DATENAME(yy, @dt))))
Номер последней недели прошлого месяца: DATEPART(wk, DATEADD(DAY, -1, DATEADD(MONTH, MONTH(@dt)-1, convert(date,DATENAME(yy, @dt)))))


Получим такое громоздкое выражение:

DATEPART(wk, @dt) + (DATEPART(wk, DATEADD(DAY, -1, DATEADD(MONTH, MONTH(@dt)-1, convert(date,DATENAME(yy, @dt)))))/DATEPART(wk,
DATEADD(MONTH, MONTH(@dt)-1, convert(date,DATENAME(yy, @dt)))) - DATEPART(wk, DATEADD(DAY, -1, DATEADD(MONTH, MONTH(@dt)-1,
convert(date,DATENAME(yy, @dt))))))*(2 - POWER(2, 1/MONTH(@dt)))


Если в переменной @dt находится дата, то это выражение возвращает неделю месяца (от 1 до 6) к которой эта дата относится. Впрочем это слишком громоздкий способ. На просторах сети встречаются варианты гораздо короче и элегантнее. Например такой:

DATEPART(week, @dt) - DATEPART(week, DATEADD(month, DATEDIFF(month, 0, @dt), 0))+1.

Вроде бы работает.
Tags:
Hubs:
Total votes 14: ↑8 and ↓6+2
Comments2

Articles