-3

Is there is a way to group dates by week of month in SQL Server?

For example

Week 2: 05/07/2012 - 05/13/2012
Week 3: 05/14/2012 - 05/20/2012

but with Sql server statement

I tried

SELECT SOMETHING, 
     datediff(wk, convert(varchar(6), getdate(), 112) + '01', getdate()) + 1 AS TIME_
FROM STATISTICS_
GROUP BY something, TIME_
ORDER BY TIME_

but it returns the week number of month. (means 3)

How to get the pair of days for current week ?

For example, now we are in third (3rd) week and I want to show 05/14/2012 - 05/20/2012

I solved somehow:

SELECT DATEADD(ww, DATEDIFF(ww,0,<my_column_name>), 0)

select DATEADD(ww, DATEDIFF(ww,0,<my_column_name>), 0)+6

Then I will get two days and I will concatenate them later.

Snake Eyes
  • 16,287
  • 34
  • 113
  • 221

4 Answers4

3

All right, bear with me here. We're going to build a temporary calendar table that represents this month, including the days from before and after the month that fall into your definition of a week (Monday - Sunday). I do this in a lot of steps to try to make the process clear, but I probably haven't excelled at that in this case.

We can then generate the ranges for the different weeks, and you can join against your other tables using that.

SET DATEFIRST 7;
SET NOCOUNT ON;

DECLARE @today SMALLDATETIME, @fd SMALLDATETIME, @rc INT;

SELECT @today = DATEADD(DAY, DATEDIFF(DAY, 0, GETDATE()), 0), -- today
    @fd = DATEADD(DAY, 1-DAY(@today), @today), -- first day of this month
    @rc = DATEPART(DAY, DATEADD(DAY, -1, DATEADD(MONTH, 1, @fd)));-- days in month

DECLARE @thismonth TABLE (
    [date]       SMALLDATETIME,
    [weekday]    TINYINT,
    [weeknumber] TINYINT 
);

;WITH n(d) AS (
    SELECT TOP (@rc+12) DATEADD(DAY, ROW_NUMBER() OVER 
    (ORDER BY [object_id]) - 7, @fd) FROM sys.all_objects
)
INSERT @thismonth([date], [weekday]) SELECT d, DATEPART(WEEKDAY, d) FROM n;

DELETE @thismonth WHERE [date] < (SELECT MIN([date]) FROM @thismonth WHERE [weekday] = 2)
                     OR [date] > (SELECT MAX([date]) FROM @thismonth WHERE [weekday] = 1);

;WITH x AS ( SELECT [date], weeknumber, rn = ((ROW_NUMBER() OVER 
   (ORDER BY [date])-1) / 7) + 1 FROM @thismonth ) UPDATE x SET weeknumber = rn;

-- now, the final query given all that (I've only broken this up to get rid of the vertical scrollbars):

;WITH ranges(w,s,e) AS (
    SELECT weeknumber, MIN([date]), MAX([date]) FROM @thismonth GROUP BY weeknumber
)
SELECT [week] = CONVERT(CHAR(10), r.s, 120) + ' - ' + CONVERT(CHAR(10), r.e, 120)

 --, SOMETHING , other columns from STATISTICS_?

 FROM ranges AS r

 -- LEFT OUTER JOIN dbo.STATISTICS_ AS s
 -- ON s.TIME_ >= r.s AND s.TIME_ < DATEADD(DAY, 1, r.e) 

 -- comment this out if you want all the weeks from this month: 
 WHERE w = (SELECT weeknumber FROM @thismonth WHERE [date] = @today)

 GROUP BY r.s, r.e --, SOMETHING
 ORDER BY [week];

Results with WHERE clause:

week
-----------------------
2012-05-14 - 2012-05-20

Results without WHERE clause:

week
-----------------------
2012-04-30 - 2012-05-06
2012-05-07 - 2012-05-13
2012-05-14 - 2012-05-20
2012-05-21 - 2012-05-27
2012-05-28 - 2012-06-03

Note that I chose YYYY-MM-DD on purpose. You should avoid regional formatting like M/D/Y especially for input but also for display. No matter how targeted you think your audience is, you're always going to have someone who thinks 05/07/2012 is July 5th, not May 7th. With YYYY-MM-DD there is no ambiguity whatsoever.

Aaron Bertrand
  • 272,866
  • 37
  • 466
  • 490
2

Create a calendar table, then you can query week numbers, first/last days of specific weeks and months etc. You can also join on it queries to get a date range etc.

Community
  • 1
  • 1
Pondlife
  • 15,992
  • 6
  • 37
  • 51
  • +1 except as per @ypercube's comment how would you mark April 30th or May 1st? Since both days will (by the OP's definition) belong to both weeks, I am not sure how you would do this - you may need to have four columns to represent this (month1, week1, month2, week2) or some other scheme I can't think of right now. I tried to address this in my answer by generating a calendar table on the fly using today's date as reference. – Aaron Bertrand May 18 '12 at 13:51
  • Well, so far the OP hasn't answered ypercube's question, so we don't know. Personally I would be very surprised to see a report where some data is double-counted, i.e. April was 1000 units, May was 1000 but April plus May was 1800. I suspect the OP will end up working with monthly reports plus weekly (week of year) reports in the end. – Pondlife May 18 '12 at 14:31
  • I don't think it's double-counting - on April 30th I want to see "this week", according to the OP's requirements thus far, I should see the same report if I run it on May 1st. – Aaron Bertrand May 18 '12 at 14:50
  • Yes, and that scenario is simple enough because each day belongs to only one week, so there's no problem. But if 30 Apr-4 May is considered to be both the last week of April and the first week of May, then you will count twice. A more usual approach (in my experience) is simply to report on "week 19" of the year. If someone asks for "the last week in April" then they have to define that term, and different people in the same company may well disagree. – Pondlife May 18 '12 at 15:03
  • I think the current week is relative to the current day. I'm suggesting that a fixed calendar table can't express which week April 30th belongs to on April 30th and on May 14th. It seems the OP just wants to report on *the current week.* In which case there is no double counting - this is the week today falls in, so this is the week we're reporting on. If you think a fixed calendar table can meet the OP's requirements, please post the code... – Aaron Bertrand May 18 '12 at 15:19
0

How about a case statement?

case when datepart(day, mydatetime) between 1 and 7 then 1
     when datepart(day, mydatetime) between 8 and 14 then 2
     ...

You'll also have to include the year & month unless you want all the week 1s in the same group.

Chris Gessler
  • 22,727
  • 7
  • 57
  • 83
0

It's not clear of you want to "group dates by week of month", or alternately "select data from a given week"

If you mean "group" this little snippet should get you 'week of month':

SELECT   <stuff>
FROM     CP_STATISTICS
WHERE    Month(<YOUR DATE COL>) = 5 --april
GROUP BY  Year(<YOUR DATE COL>), 
          Month(<YOUR DATE COL>),
          DATEDIFF(week, DATEADD(MONTH, DATEDIFF(MONTH, 0, <YOUR DATE COL>), 0)
           , <YOUR DATE COL>) +1

Alternately, if you want "sales for week 1 of April, ordered by date" You could do something like..

DECLARE @targetDate datetime2 = '5/3/2012'
DECLARE @targetWeek int = DATEDIFF(week, DATEADD(MONTH, 
                             DATEDIFF(MONTH, 0, @targetDate), 0), @targetDate) +1


SELECT   <stuff>
FROM     CP_STATISTICS
WHERE    MONTH(@targetDate) = Month(myDateCol) AND
         YEAR(@targetDate)  = Year (myDateCol) AND 
         @targetWeek = DATEDIFF(week, DATEADD(MONTH, 
                                DATEDIFF(MONTH, 0, myDateCol), 0), myDateCol) +1
ORDER BY myDateCol

Note, things would get more complicated if you use non-standard weeks, or want to reach a few days into an earlier month for weeks that straddle a month boundary.

EDIT 2

From looking at your 'solved now' section. I think your question is "how do I get data out of a table for a given week?"

Your solution appears to be:

DECLARE @targetDate datetime2 = '5/1/2012'
DECLARE @startDate  datetime2 = DATEADD(ww, DATEDIFF(ww,0,targetDate), 0)
DECLARE @endDate    datetime2 = DATEADD(ww, DATEDIFF(ww,0,@now), 0)+6

SELECT  <stuff>
FROM    STATISTICS_
WHERE   dateStamp >= @startDate AND dateStamp <= @endDate

Notice how if the date is 5/1 this solution results in a start date of '4/30/2012'. I point this out because your solution crosses month boundaries. This may or may not be desirable.

EBarr
  • 11,826
  • 7
  • 63
  • 85
  • I have already `datediff(wk, convert(varchar(6), getdate(), 112) + '01', getdate()) + 1 AS TIME_` in my query – Snake Eyes May 18 '12 at 12:42
  • I see that, but you're using 'getdate()' which always returns the current time. Grouping a query by the current time won't result in any grouping. Do you want something like : "get sales, sumarized by week for april?" Alternately, do you want to show "sales for the 3rd week of april?" – EBarr May 18 '12 at 12:44
  • I think the OP wants to use GETDATE() only to determine what the current week is. Not 100% sure but sounds like he just wants to report on the current week. So probably should be part of WHERE clause, with a slightly different calculation for the display. – Aaron Bertrand May 18 '12 at 12:45
  • Agreed. I've re-read the question a few times and I'm a bit confused too. – EBarr May 18 '12 at 12:46
  • @MichaelSwan - I can see the edit. SO has a great version history tool if you click on the time listed for the most recent edit. Unfortunately, you're question is still opaque (as you can see from all the comments flying by). It's not clear how your solution applies to the problem. I'm sure future coders would appreciate if you could take time to edit/clean-up/clarify your post. – EBarr May 18 '12 at 13:07