Есть ли удобный способ сравнить данные за неделю в SQL?

#sql-server #tsql #aggregate #aggregate-functions

#sql-server #tsql #агрегировать #агрегатные функции

Вопрос:

У меня есть таблица, полная ежедневных сводных данных, но мне иногда нужно извлекать еженедельные сводные данные и предоставлять информацию об увеличении или уменьшении. По этой причине я рассматривал возможность использования функции T-SQL DATEPART для получения информации о номере недели и году для дат.

Например, я могу получить следующую информацию, используя сегодняшнюю дату (9/11/2020):

 @nowWeekNumber int = datepart(wk,@today),  --yields 37
@nowYear int = datepart(year,@today),      --yields 2020
  

Используя эту логику, я мог бы затем собрать информацию о записях, где год 2020, а номер недели 36, а затем я мог бы сравнить эти цифры, чтобы получить еженедельное увеличение / уменьшение. (Или, может быть, я бы сравнил недели 35 и 36, чтобы убедиться, что я имею дело с целыми неделями, но вы понимаете картину)

Однако, если дата 2021-01-03, это вернет 2021 год и номер недели, равный 2. Если я вычту неделю, я получу 2021 год и номер недели 1. Этот номер недели будет содержать только 1 и 2 января, потому что с 12/27 по 12/31 считаются 2020 годом, а номер недели 53 (даже если календарная неделя с 12/27 по 1/2).

Другими словами, я не думаю, что смогу использовать weekNumber для сбора еженедельных данных, хотя это было бы довольно удобно. Я знаю, что могу использовать функции DATEADD для получения начальной и конечной даты для последовательных недель, и затем я могу собирать сводные данные для записей между этими датами, но есть ли более удобный способ сделать это?

Комментарии:

1. Из вопроса не совсем ясно, каково определение недели для целей вашей отчетности. Всегда ли неделя начинается и заканчивается в один и тот же день (например, понедельник), или это может быть за семь дней до / после произвольной даты?

Ответ №1:

Почему бы вам не рассмотреть возможность использования dateDiff в качестве ключевой функции? Как…

 select dateDiff(wk, 0, getDate())
  

Возвращает одно целое число за всю неделю (6297 для '20200911' ) и :

 select                dateAdd(wk, dateDiff(wk, 0, getDate()), 0),
       dateAdd(dd, 6, dateAdd(wk, dateDiff(wk, 0, getDate()), 0))
  

или

 select                dateAdd(wk, 6297, 0),
       dateAdd(dd, 6, dateAdd(wk, 6297, 0))
  

выдает 1-й и последний день этой недели.

Комментарии:

1. Да, это то, где я сейчас нахожусь. Я могу вручную собрать даты начала и окончания для данной недели. Я просто подумал, что было бы удобнее собирать даты, где неделя равна xyz, и я почти могу это сделать, за исключением того, что логика разваливается для дат около 1 января. Я полагаю, я мог бы использовать некоторую логику в этом ответе, чтобы получить группу записей, где dateDiff(wk, 0, dateValue) = определенное значение.

Ответ №2:

Вы можете использовать DATEPART но вместо wk вы можете использовать iso week. Тогда у вас не возникнет проблемы с разделением недели на 2. Для уверенности также используйте SET DATEFIRST , чтобы точно определить, в какой день начинается неделя.

 SET DATEFIRST 1; --use monday as first day of the week
SELECT datepart(iso_week,'2021-01-01');
SELECT datepart(iso_week,'2021-01-03');
SELECT datepart(iso_week,'2021-01-04');
  

Другой вариант — создать свою собственную таблицу календаря и присоединить ее к своей ежедневной таблице.

РЕДАКТИРОВАТЬ: для недели, начинающейся в воскресенье

 SET DATEFIRST 7;
SELECT DATEPART(WEEK, DATEADD( DAY, 1-DATEPART(WEEKDAY,'2020-12-27'),'2020-12-27' ) )
SELECT DATEPART(WEEK, DATEADD( DAY, 1-DATEPART(WEEKDAY,'2020-12-28'),'2020-12-28' ) )
SELECT DATEPART(WEEK, DATEADD( DAY, 1-DATEPART(WEEKDAY,'2021-01-01'),'2021-01-01' ) )
SELECT DATEPART(WEEK, DATEADD( DAY, 1-DATEPART(WEEKDAY,'2021-01-02'),'2021-01-02' ) )
  

Комментарии:

1. похоже, у iso_week та же проблема. 1/3/2021 имеет номер iso_week равный 53, но более ранняя неделя с 27.12.2020 по 1/2/2021 имеет значения iso_week 52 и 53 соответственно.

2. На самом деле это другая проблема. Похоже, что DATEFIRST не влияет на isoweek. Я думаю, это имеет смысл, потому что это международный стандарт. Вот почему неделя iso всегда начинается в понедельник.

3. Другой вариант — затем взять номер недели первого дня недели (например, воскресенье) для всех дней этой недели. Нравится DATEPART(WEEK, DATEADD( DAY,1 -DATEPART(WEEKDAY,'2020-12-27'),'2020-12-27' ) )