Как получить понедельник, воскресенье на основе номера недели в SQL Server? - PullRequest
4 голосов
/ 02 февраля 2011

У меня есть номер недели (например, 23), и я хотел бы получить даты понедельника и воскресенья этой недели.
Я предполагаю понедельник в качестве первого дня недели (например, у меня SET DATEFIRST 1в моем сценарии).

Ответы [ 5 ]

3 голосов
/ 02 февраля 2011
DECLARE @startweek1 datetime
SET DATEFIRST 1

--first monday of year
SELECT @startweek1 = DATEADD(day, 8-DATEPART(weekday, '2011-01-01'), '2011-01-01')

--day based
SELECT
    DATEADD(day, 22 * 7, @startweek1) AS MondayWeek23,
    DATEADD(day, 23 * 7 -1 , @startweek1) AS SundayWeek23

--week based
SELECT
    DATEADD(week, 22, @startweek1) AS MondayWeek23,
    DATEADD(day, -1, DATEADD(week, 23 , @startweek1)) AS SundayWeek23

Edit:

Это решение работает, если неделя 1 не начинается в день 1, как сказал Андомар

Редактировать 2:

Согласно Википедии : 2008-12-29 - это 1-й день 1-й недели 2009 года в ISO.

И номера недели меняются , как сказал Андомар

Киберкиви упомянул, что этот код неверен для 2007 года: он гораздо чаще, чем этот. То же самое относится и к его коду, который соответствует 2007 году, но в остальном он в равной степени неправильный.

1 голос
/ 02 февраля 2011
Declare @StartDate datetime;
Set @StartDate = '20110101';

With StartOfWeek As
    (
    Select DateAdd(
        week
        , 23
        , DateAdd(
            d 
            , -(DatePart(dw, @StartDate) - 1)
            , @StartDate
            ) ) As Sunday
    )
Select Sunday, DateAdd(d,1,Sunday) As Monday
From StartOfWeek
0 голосов
/ 03 февраля 2011

Календарная таблица делает этот тип запроса даты довольно простым.В таблице календаря только соответствующие данные календаря рассчитываются и сохраняются во время загрузки, а не вычисляются во время выполнения.В зависимости от приложения, это может значительно ускорить процесс.Этот способ не зависит от БДМ;он просто использует литералы в предложении WHERE.

select cal_date from calendar
where iso_year = 2011 
  and iso_week = 23
  and cal_dow in ('Mon', 'Sun');

И еще один способ, который основан только на наличии таблицы дат.

select cal_date from calendar
where extract(isoyear from cal_date) = 2011 
  and extract(week from cal_date) = 23 
  and (extract(isodow from cal_date) = 1 or
       extract(isodow from cal_date) = 7);

EXTRACT () - это стандартный SQL, ноя не уверен, являются ли имена подполей isoyear, week, isodow стандартным SQL.

0 голосов
/ 02 февраля 2011

Примечание: Любой ответ, который начинается с предположения, что год 2011, также может принимать статическую первую дату, т. Е. Заменить @firstMon в моем последнем запросе ниже только статической датой '20101227' .

SET DATEFIRST 1

declare @targetYear int
declare @targetWeek int
select @targetYear = 2011, @targetWeek = 23

declare @firstMon datetime
set @firstMon = dateadd(d,1-datepart(dw, right(@targetYear,4)+'0101'),right(@targetYear,4)+'0101')

select
    MonOfTargetWeek = dateadd(week, @targetWeek-1, @firstMon),
    SunOfTargetWeek = dateadd(week, @targetWeek-1, @firstMon+6)

И реальный, последний запрос, который возвращает Mon / Sun со значением NULL, если он не существует на этой неделе, например, Mon из частичной недели или Sun из частичной недели 53

;with tmp(Mon, Sun) as (
select
    MonOfTargetWeek = dateadd(week, @targetWeek-1, @firstMon),
    SunOfTargetWeek = dateadd(week, @targetWeek-1, @firstMon+6)
)
select
    RealMonday = case when Year(Mon)=@targetYear then Mon end,
    RealSunday = case when Year(Sun)=@targetYear then Sun end
from tmp

Для заявления GBN о том, что этот ответ неправильный (в пределах его ответа), я представлю ниже подтверждение правильности

Я превратил код в функцию.

CREATE function dbo.getMonSunForWeek(@targetYear int, @targetWeek int)
returns table as return
with pre(firstMon) as (
    select dateadd(d,1-datepart(dw, right(@targetYear,4)+'0101'),right(@targetYear,4)+'0101'))
, tmp(Mon, Sun) as (
select
    dateadd(week, @targetWeek-1, firstMon),
    dateadd(week, @targetWeek-1, firstMon+6)
    from pre
)
select
    Mon, Sun,
    RealMonday = case when Year(Mon)=@targetYear then Mon end,
    RealSunday = case when Year(Sun)=@targetYear then Sun end
from tmp
GO

И ниже я представляю ВЕСЬ диапазон лет и недель (1-53) для лет с 1950 по 2047 год. В случае КАЖДЫЙ ОДИН , когда был определен понедельник / воскресенье, и работая в обратном направлении, используя DATEPART(week), SQL Server соглашается с нумерацией недель из функции.

set datefirst 1;
select
    [Year]  = years.number,
    [Week]  = weeks.number,
    [Mon]   = fun.realmonday,
    [Sun]   = fun.realsunday,
    [WeekX] = isnull(datepart(week, fun.realmonday), datepart(week, fun.realsunday)),
    [MonX]  = fun.Mon,
    [SunX]  = fun.Sun
from master..spt_values years
inner join master..spt_values weeks on weeks.type='P' and weeks.number between 1 and 53
cross apply dbo.getMonSunForWeek(years.number, weeks.number) fun
where years.type='P' and years.number between 1950 and 2047
order by [year], [Week]

Выход на смену 2005-2006 гг.

                                                 when including prior/next year
Year Week Mon-of-week Sun-of-week datepart(week) Mon  -and-  Sun 
2005 52   2005-12-19  2005-12-25  52             2005-12-19  2005-12-25
2005 53   2005-12-26  NULL        53             2005-12-26  2006-01-01
2006  1   NULL        2006-01-01   1             2005-12-26  2006-01-01
2006  2   2006-01-02  2006-01-08   2             2006-01-02  2006-01-08
0 голосов
/ 02 февраля 2011

Вы можете попробовать некоторую логику dateadd ...

dateadd(week, 23, '2011-01-01')

dateadd(day, 7, dateadd(week, 23, '2011-01-01'))

ОБНОВЛЕНИЕ:

select dateadd(day, 23, dateadd(week, 23, '2011-01-01')) , Datename(weekday,dateadd(day, 23, dateadd(week, 23, '2011-01-01')));
// RETURNS 7/4/2011,    Monday
select  Datename(weekday,dateadd(day, 23, dateadd(week, 23, '2011-01-01')));
// RETURNS  Monday

ПРИМЕР ОТ: http://msdn.microsoft.com/en-us/library/ms186819.aspx

DECLARE @datetime2 datetime2 = '2007-01-01 13:10:10.1111111'
SELECT 'year', DATEADD(year,1,@datetime2)
UNION ALL
SELECT 'quarter',DATEADD(quarter,1,@datetime2)
UNION ALL
SELECT 'month',DATEADD(month,1,@datetime2)
UNION ALL
SELECT 'dayofyear',DATEADD(dayofyear,1,@datetime2)
UNION ALL
SELECT 'day',DATEADD(day,1,@datetime2)
UNION ALL
SELECT 'week',DATEADD(week,1,@datetime2)
UNION ALL
SELECT 'weekday',DATEADD(weekday,1,@datetime2)
UNION ALL
SELECT 'hour',DATEADD(hour,1,@datetime2)
UNION ALL
SELECT 'minute',DATEADD(minute,1,@datetime2)
UNION ALL
SELECT 'second',DATEADD(second,1,@datetime2)
UNION ALL
SELECT 'millisecond',DATEADD(millisecond,1,@datetime2)
UNION ALL
SELECT 'microsecond',DATEADD(microsecond,1,@datetime2)
UNION ALL
SELECT 'nanosecond',DATEADD(nanosecond,1,@datetime2);
/*
Year         2008-01-01 13:10:10.1111111
quarter      2007-04-01 13:10:10.1111111
month        2007-02-01 13:10:10.1111111
dayofyear    2007-01-02 13:10:10.1111111
day          2007-01-02 13:10:10.1111111
week         2007-01-08 13:10:10.1111111
weekday      2007-01-02 13:10:10.1111111
hour         2007-01-01 14:10:10.1111111
minute       2007-01-01 13:11:10.1111111
second       2007-01-01 13:10:11.1111111
millisecond  2007-01-01 13:10:10.1121111
microsecond  2007-01-01 13:10:10.1111121
nanosecond   2007-01-01 13:10:10.1111111
*/
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...