Чтобы ответить, почему вы получаете понедельник, а не воскресенье:
Вы добавляете количество недель к дате 0. Что такое дата 0? 1900-01-01. Какой был день 1900-01-01? Понедельник. Итак, в своем коде вы говорите, сколько недель прошло с понедельника, 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
(или ваш код выполняется для пользователя с другой настройкой) - при условии, что вы все еще хотите воскресенье независимо от текущей настройки. Если вы хотите, чтобы эти два ответа совпали, то вам следует использовать функцию, которая делает зависимой от настройки 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
Я могу при желании передать подробности моих тестов - останавливаясь здесь, поскольку это уже становится довольно многословным. Я был немного удивлен, увидев, что Курт оказался самым быстрым на верхнем уровне, учитывая количество вычислений и встроенный код. Может быть, я проведу более тщательные тесты и напишу в блоге об этом ... если вы, ребята, не возражаете против того, чтобы я опубликовал ваши функции в другом месте.