Я пытаюсь сгруппировать записи по неделям, сохраняя агрегированную дату как первый день недели. Однако стандартный метод, который я использую для округления дат, не работает правильно с неделями (хотя он работает с днями, месяцами, годами, кварталами и любыми другими временными рамками, к которым я его применил).
Вот SQL:
select "start_of_week" = dateadd(week, datediff(week, 0, getdate()), 0);
Это возвращается 2011-08-22 00:00:00.000
, это понедельник, а не воскресенье. Выбор @@datefirst
возврата 7
, который является кодом для воскресенья, поэтому, насколько мне известно, сервер настроен правильно.
Я могу легко обойти это, изменив приведенный выше код на:
select "start_of_week" = dateadd(week, datediff(week, 0, getdate()), -1);
Но то, что я вынужден сделать такое исключение, меня немного смущает. Также извиняюсь, если это повторяющийся вопрос. Я нашел несколько связанных вопросов, но ни один из них не касался конкретно этого аспекта.
источник
(@@DATEFIRST + DATEPART(DW, @SomeDate)) % 7
@@datefirst
я думаю, остается постоянным независимо от настройки. С понедельником = 2.Ответы:
Чтобы ответить, почему у вас понедельник, а не воскресенье:
Вы добавляете число недель к дате 0. Что такое дата 0? 1900-01-01. Какой был день 01.01.1900? Понедельник. Итак, в вашем коде вы говорите, сколько недель прошло с понедельника, 1 января 1900 года? Назовем это [n]. Хорошо, теперь добавьте [n] недель к понедельнику, 1 января 1900 года. Вы не должны удивляться, что это окажется понедельник.
DATEADD
понятия не имеет, что вы хотите добавить недели, но только до тех пор, пока не дойдете до воскресенья, он просто добавляет 7 дней, а затем добавляет еще 7 дней, ... точно так же, какDATEDIFF
распознает только границы, которые были пересечены. Например, они оба возвращают 1, хотя некоторые люди жалуются, что должна быть встроена разумная логика для округления в большую или меньшую сторону:Чтобы ответить, как получить воскресенье:
Если вы хотите воскресенье, выберите базовую дату не понедельник, а воскресенье. Например:
Это не сломается, если вы измените
DATEFIRST
настройку (или ваш код работает для пользователя с другой настройкой) - при условии, что вы все еще хотите воскресенье, независимо от текущей настройки. Если вы хотите, чтобы эти два ответа были jive, вам следует использовать функцию, которая зависит отDATEFIRST
настройки, напримерИтак, если вы измените
DATEFIRST
настройку на понедельник, вторник, что у вас, поведение изменится. В зависимости от того, какое поведение вы хотите, вы можете использовать одну из этих функций:...или...
Теперь у вас есть много альтернатив, но какая из них работает лучше всего? Я был бы удивлен, если бы произошли какие-либо серьезные различия, но я собрал все ответы, предоставленные до сих пор, и провел их через два набора тестов - один дешевый, а другой дорогой. Я измерил клиентскую статистику, потому что я не вижу, чтобы ввод-вывод или память влияли на производительность здесь (хотя они могут иметь значение в зависимости от того, как используется функция). В моих тестах результаты следующие:
Запрос о "дешевом" назначении:
"Дорогой" запрос о назначении:
При желании я могу передать подробности моих тестов - остановимся здесь, поскольку это уже становится довольно длинным. Я был немного удивлен, увидев, что Curt оказался самым быстрым на высоком уровне, учитывая количество вычислений и встроенный код. Может быть, я проведу более тщательные тесты и напишу об этом в блоге ... если вы, ребята, не возражаете против публикации ваших функций в другом месте.
источник
Для этого необходимо получить:
Понедельник = 1 и воскресенье = 7:
Воскресенье = 1 и суббота = 7:
Выше был аналогичный пример, но благодаря двойному «% 7» он был бы намного медленнее.
источник
select (datediff(dd,5,cal.D_DATE)%7 + 1)
иselect (datediff(dd,6,cal.D_DATE)%7 + 1)
Для тех, кому нужен ответ на работе, а функция создания запрещена вашим администратором баз данных, подойдет следующее решение:
Это дает начало той недели. Здесь я предполагаю, что воскресенье - начало недель. Если вы думаете, что понедельник - это начало, вам следует использовать:
источник
У меня это прекрасно работает:
источник
Погуглил этот скрипт:
http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=47307
источник
Может вам это понадобится:
Или
Функция
источник
DATEPART(DW
зависит от@@datefirst
DATE
тогда вам не нужно будет выполнять какие-либо неоптимальные преобразования вVARCHAR
и обратно, просто чтобы удалить любой случайный компонент времени, который передается.Time
значений.источник
Для основного (воскресенье текущей недели)
Если на прошлой неделе:
Внутри мы создали функцию, которая делает это, но если вам нужно быстро и грязно, это сработает.
источник
Поскольку юлианская дата 0 - это понедельник, просто добавьте количество недель к воскресенью, то есть за день до -1. Например. выберите dateadd (wk, dateiff (wk, 0, getdate ()), - 1)
источник
Это моя логика. Установите для начала недели понедельник, затем вычислите, какой день недели соответствует данному дню, а затем с помощью DateAdd и Case I вычислите, какой была бы дата в предыдущий понедельник этой недели.
источник
У меня нет проблем с каким-либо из приведенных здесь ответов, однако я думаю, что мой вариант намного проще реализовать и понять. Я не проводил на нем никаких тестов производительности, но им можно пренебречь.
Итак, я получил свой ответ из того факта, что даты хранятся на сервере SQL как целые числа (я говорю только о компоненте даты). Если вы мне не верите, попробуйте этот SELECT CONVERT (INT, GETDATE ()) и наоборот.
Теперь, зная это, вы можете решать несколько классных математических уравнений. Вы могли бы придумать лучший, но вот мой.
источник
@@DATEFIRST
тоже 7, но если у вас@testDate
начало недели, то это возвращает дату, которая была накануне.У меня была похожая проблема. Учитывая дату, я хотел получить дату понедельника этой недели.
Я использовал следующую логику: найдите номер дня недели в диапазоне от 0 до 6, затем вычтите его из исходной даты.
Я использовал: DATEADD (день, - (DATEPART (день недели,) + 5)% 7,)
Поскольку DATEPRRT (день недели,) возвращает 1 = воскресеньеe ... 7 = субботу, DATEPART (день недели) + 5)% 7 возвращает 0 = понедельник ... 6 = воскресенье.
Вычитание этого количества дней из исходной даты дает предыдущий понедельник. Эту же технику можно использовать для любого начального дня недели.
источник
Я нашел это простым и полезным. Работает, даже если первый день недели - воскресенье или понедельник.
ЗАЯВИТЬ @BaseDate как дату
НАБОР @BaseDate = GETDATE ()
ЗАЯВИТЬ @FisrtDOW КАК ДАТУ
ВЫБРАТЬ @FirstDOW = DATEADD (d, DATEPART (WEEKDAY, @ BaseDate) * -1 + 1, @BaseDate)
источник
Возможно, я здесь слишком упрощаю, и это может быть так, но, похоже, это работает для меня. Проблем с этим пока не сталкивался ...
источник
SET DATEFIRST
.DATEFIRST
(вот уже три с половиной года), и до сих пор нет. И вам также следует избегать региональных форматов, напримерm/d/y
, даже в сценариях, где m и d совпадают.