Чтобы ответить, почему у вас понедельник, а не воскресенье:
Вы добавляете число недель к дате 0. Что такое дата 0? 1900-01-01. Какой был день 01.01.1900? Понедельник. Итак, в вашем коде вы говорите, сколько недель прошло с понедельника, 1 января 1900 года? Назовем это [n]. Хорошо, теперь добавьте [n] недель к понедельнику, 1 января 1900 года. Вы не должны удивляться, что это окажется понедельник. DATEADD
понятия не имеет, что вы хотите добавить недели, но только до тех пор, пока не дойдете до воскресенья, он просто добавляет 7 дней, а затем добавляет еще 7 дней, ... точно так же, как DATEDIFF
распознает только границы, которые были пересечены. Например, они оба возвращают 1, хотя некоторые люди жалуются, что должна быть встроена разумная логика для округления в большую или меньшую сторону:
SELECT DATEDIFF(YEAR, '2010-01-01', '2011-12-31');
SELECT DATEDIFF(YEAR, '2010-12-31', '2011-01-01');
Чтобы ответить, как получить воскресенье:
Если вы хотите воскресенье, выберите базовую дату не понедельник, а воскресенье. Например:
DECLARE @dt DATE = '1905-01-01';
SELECT [start_of_week] = DATEADD(WEEK, DATEDIFF(WEEK, @dt, CURRENT_TIMESTAMP), @dt);
Это не сломается, если вы измените DATEFIRST
настройку (или ваш код работает для пользователя с другой настройкой) - при условии, что вы все еще хотите воскресенье, независимо от текущей настройки. Если вы хотите, чтобы эти два ответа были jive, вам следует использовать функцию, которая зависит от DATEFIRST
настройки, например
SELECT DATEADD(DAY, 1-DATEPART(WEEKDAY, CURRENT_TIMESTAMP), CURRENT_TIMESTAMP);
Итак, если вы измените DATEFIRST
настройку на понедельник, вторник, что у вас, поведение изменится. В зависимости от того, какое поведение вы хотите, вы можете использовать одну из этих функций:
CREATE FUNCTION dbo.StartOfWeek1 -- always a Sunday
(
@d DATE
)
RETURNS DATE
AS
BEGIN
RETURN (SELECT DATEADD(WEEK, DATEDIFF(WEEK, '19050101', @d), '19050101'));
END
GO
...или...
CREATE FUNCTION dbo.StartOfWeek2 -- always the DATEFIRST weekday
(
@d DATE
)
RETURNS DATE
AS
BEGIN
RETURN (SELECT DATEADD(DAY, 1-DATEPART(WEEKDAY, @d), @d));
END
GO
Теперь у вас есть много альтернатив, но какая из них работает лучше всего? Я был бы удивлен, если бы произошли какие-либо серьезные различия, но я собрал все ответы, предоставленные до сих пор, и провел их через два набора тестов - один дешевый, а другой дорогой. Я измерил клиентскую статистику, потому что я не вижу, чтобы ввод-вывод или память влияли на производительность здесь (хотя они могут иметь значение в зависимости от того, как используется функция). В моих тестах результаты следующие:
Запрос о "дешевом" назначении:
Function - client processing time / wait time on server replies / total exec time
Gandarez - 330/2029/2359 - 0:23.6
me datefirst - 329/2123/2452 - 0:24.5
me Sunday - 357/2158/2515 - 0:25.2
trailmax - 364/2160/2524 - 0:25.2
Curt - 424/2202/2626 - 0:26.3
"Дорогой" запрос о назначении:
Function - client processing time / wait time on server replies / total exec time
Curt - 1003/134158/135054 - 2:15
Gandarez - 957/142919/143876 - 2:24
me Sunday - 932/166817/165885 - 2:47
me datefirst - 939/171698/172637 - 2:53
trailmax - 958/173174/174132 - 2:54
При желании я могу передать подробности моих тестов - остановимся здесь, поскольку это уже становится довольно длинным. Я был немного удивлен, увидев, что Curt оказался самым быстрым на высоком уровне, учитывая количество вычислений и встроенный код. Может быть, я проведу более тщательные тесты и напишу об этом в блоге ... если вы, ребята, не возражаете против публикации ваших функций в другом месте.
(@@DATEFIRST + DATEPART(DW, @SomeDate)) % 7
@@datefirst
я думаю, остается постоянным независимо от настройки. С понедельником = 2.