SQL 2008, week numbers and years - sql-server-2008

Having some problems getting the information I need from the database. I fell foul of the week vs iso_week setting in DATEPART but have now got the problem of getting the year from it. The nub of the issue is that when I run
SELECT datePart(iso_week, '2014-12-29');
SELECT datePart(week, '2014-12-29');
SELECT datePart(YEAR, '2014-12-29');
I get Wk 1 for the first query, Wk 53 for the second. (first being what I want) but the year is still 2014. So if I do
SELECT CONCAT('WK', RIGHT(CONCAT('00', DATEPART(iso_week, '2014-12-29')), 2), ' ', DATEPART(year, '2014-12-29'))
I get WK01 2014 instead of WK01 2015.
Anyone know a way I can get the right ISO week and the associated year for a date?
Edit:
See if I can clear up a bit here.
Not expecting the above to return 2015 instead of 2014, that would be daft. What I need is a way to know that the week number returned is actually in the next year. I did consider doing a straight if week > 52 then its wk 1 in the next year, but that seems a little sledgehammerish and I am not sure it will actually work.

You should understand how the ISO_WEEK is calculated. From Microsoft:
ISO 8601 includes the ISO week-date system, a numbering system for
weeks. Each week is associated with the year in which Thursday occurs.
For example, week 1 of 2004 (2004W01) ran from Monday 29 December 2003
to Sunday, 4 January 2004. The highest week number in a year might be
52 or 53. This style of numbering is typically used in European
countries/regions, but rare elsewhere.
Hence your DATEPART(YEAR, ...) you should pass in the Thursday on or after December 29, 2014.

Try this
SELECT CONCAT('WK', RIGHT(CONCAT('00', DATEPART(iso_week, '2014-12-29') % 53), 2), ' ', DATEPART(year, '2014-12-29') + DATEPART(iso_week, '2014-12-29') % 53)

Had someone set me on the right track with some simple logic. I was trying to over complicate things. This seems to work for everything I can throw at it.
DECLARE #date AS DATETIME
SET #date = '2014-12-29'
SELECT
CAST(
CASE
WHEN MONTH(#Date) = 1 AND DATEPART(iso_week, #date) > 51 THEN YEAR(#Date) - 1
WHEN MONTH(#Date) = 12 AND DATEPART(iso_week, #date) = 1 THEN YEAR(#Date) + 1
ELSE YEAR(#Date)
END
AS VARCHAR(4)) AS Year
,
RIGHT('00' + CAST(DATEPART(iso_week, #date) AS VARCHAR(2)), 2) AS Week
It seems to cover true wk53 occurrences and incorrect ones.

Related

SSRS Returning incorrect WW

I have an SSRS report (2008R2 3.0) that uses parameter filters for workweek in the format YYYYWW. In 2018 there were 52 work weeks based on the ISO standard (source) .
For default parameter value I select the last WW using the following formula:
DATEPART(DateInterval.Year, DATEADD("d", -7, now())) & FORMAT(DATEPART(DateInterval.WeekOfYear, DATEADD("d", -7, now())), "00")
For some odd reason this returned 201853. From what I understand it should be 201852 given the date when ran was 20180106.
I know in T-SQL I can define return type using ISO_WEEK but is there an equivalent for DateInterval in SSRS?
To simplify:
DATEPART(DateInterval.WeekOfYear, DATEADD("d",-7,now()))
Returns 53 instead of 52 when ran from 201801-201807.
Any idea as to why this is returning the wrong WeekOfYear value?
I think this may be due to your Regional First Day of the Week setting on your server.
If your First Day of the Week is Saturday, there are 53 weeks in the 2018. You can set the first day of the week with the DATEFIRST setting in SQL Server.
Use SELECT ##DATEFIRST to determine your servers setting.
This example code will show the difference.
SET DATEFIRST 7
SELECT ##DATEFIRST
SELECT DATEPART(WEEK, '2018-12-30')
SET DATEFIRST 1
SELECT ##DATEFIRST
SELECT DATEPART(WEEK, '2018-12-30')
For your query, you may need to set the first day of the week to Sunday with SET DATEFIRST 1.
MS Docs: DATEFIRST

How to convert a string 'YEARWEEK' with STR_TO_DATE to a correct date?

I am trying to convert values like 201701 to 2017-01-01 which is the first day of the first week of 2017, I have tried this
SELECT STR_TO_DATE('201701', '%Y%v')
because DATE_FORMAT(DATE, '%Y%v') works like YEARWEEK() does, but instead of getting the first day of the corresponding week I get
2017-00-00
which isn't even a valid date of course.
Try this:
SELECT
DATE_ADD(MAKEDATE(LEFT(d, 4), 1),
INTERVAL RIGHT(d, 2) - 1 WEEK)
FROM
(SELECT '201701' d) t
MAKEDATE() generate given day of the year (in this case 1st). and add weeks offset to it to get week's start.
If you add 'Monday' to beneath script in some way, you get what you are looking for I guess (the date of the first day of the specified week).
SELECT STR_TO_DATE('201701 Monday',
'%X%V %W')
--returns: 2017-01-02
PS: 2017-1-1 (Sunday) is in week 52 of 2016. 2017-1-2 (Monday) is in week 1 of 2017. Weeks start on Mondays.

working days calculation query - working but not able to understand

I wanted to calculate number of working days for an year (excluding only saturdays and sundays) and got this query from web and its working..but i am not able to understand this query from MID part..can anyone help me to understand this...
SELECT 5 * (DATEDIFF('2015-12-31', '2015-01-01') DIV 7) +
MID('0123444401233334012222340111123400012345001234550', 7 * WEEKDAY('2015-12-31') +
WEEKDAY('2015-01-01') + 1, 1)
i am not able to understand from
MID('0123444401233334012222340111123400012345001234550', 7 * WEEKDAY('2015-12-31') +
WEEKDAY('2015-01-01') + 1, 1)
WEEKDAY('2015-12-31') and WEEKDAY('2015-01-01') are computing the day of the week for the start and end of the year, with 0 = Monday and 6 = Sunday. For the year 2015, these are both 6 (Sunday). So the middle argument of MID is 7 * 6 + 6 + 1, or 49.
The 49th character of the long string is 0. So this means it will add zero to the rest of the expression.
The purpose of this expression is to adjust the weekday count according to what days of the week the first and last day of the year are. In 2015, neither one is a weekday, so it adds zero. Now consider 2016. Jan 1 2016 is a Monday (weekday=1) and Dec 31 2016 is a Tuesday (weekday=2). 7*1+2+1 equals 10. The tenth character of the long string is 1. So it will add one weekday ... intuitively we can see this happens because 2016 is a leap year, so there's one more day in the year. And so on. For each possible beginning and ending day of the year, the long string encodes an adjustment of how many weekdays to add. I imagine that the string was discovered by trial and error.

MDX to get last Sunday and Saturday

I built a SSRS 2005 report on a SSAS 2005 cube. The report has start date and end date parameters from Time dimension. I need to set the default values to be last Sunday and last Saturday separately. (financial week is from Sunday to Saturday)
E.g.
Start date: [Time].[Day].&[20140309]
End date: [Time].[Day].&[20140315]
How can I do it dynamically? I mean for this week is above dates, but for next week, it should be 16 March, 22 March. I know how to do it in T-SQL, which will involve some calculation with system date, but MDX?
You can use some VBA Date functions available in MDX:
StrToMember('[Time].[Day].&['
+ Format(DateAdd('d', - DatePart('w', Now(), 1), Now()), 'yyyyMMdd')
+ ']'
)
should give you the last Saturday before today, and
StrToMember('[Time].[Day].&['
+ Format(DateAdd('d', - DatePart('w', Now(), 1) - 6, Now()), 'yyyyMMdd')
+ ']'
)
the last Sunday before that.
The second argument of DateAdd('d', ...) is the number of days to add. And as this is negative here, we go back in time that many days. DatePart('w', ...) returns the weekday number (Sunday = 1, Monday = 2, ...). Thus, if you subtract "weekday number" days from today, luckily you are already at last Saturday. And subtracting six more days, you arrive at the last Sunday before that.

How to group by week in MySQL?

Oracle's table server offers a built-in function, TRUNC(timestamp,'DY'). This function converts any timestamp to midnight on the previous Sunday. What's the best way to do this in MySQL?
Oracle also offers TRUNC(timestamp,'MM') to convert a timestamp to midnight on the first day of the month in which it occurs. In MySQL, this one is straightforward:
TIMESTAMP(DATE_FORMAT(timestamp, '%Y-%m-01'))
But this DATE_FORMAT trick won't work for weeks. I'm aware of the WEEK(timestamp) function, but I really don't want week number within the year; this stuff is for multiyear work.
You can use both YEAR(timestamp) and WEEK(timestamp), and use both of the these expressions in the SELECT and the GROUP BY clause.
Not overly elegant, but functional...
And of course you can combine these two date parts in a single expression as well, i.e. something like
SELECT CONCAT(YEAR(timestamp), '/', WEEK(timestamp)), etc...
FROM ...
WHERE ..
GROUP BY CONCAT(YEAR(timestamp), '/', WEEK(timestamp))
Edit: As Martin points out you can also use the YEARWEEK(mysqldatefield) function, although its output is not as eye friendly as the longer formula above.
Edit 2 [3 1/2 years later!]:
YEARWEEK(mysqldatefield) with the optional second argument (mode) set to either 0 or 2 is probably the best way to aggregate by complete weeks (i.e. including for weeks which straddle over January 1st), if that is what is desired. The YEAR() / WEEK() approach initially proposed in this answer has the effect of splitting the aggregated data for such "straddling" weeks in two: one with the former year, one with the new year.
A clean-cut every year, at the cost of having up to two partial weeks, one at either end, is often desired in accounting etc. and for that the YEAR() / WEEK() approach is better.
Figured it out... it's a little cumbersome, but here it is.
FROM_DAYS(TO_DAYS(TIMESTAMP) -MOD(TO_DAYS(TIMESTAMP) -1, 7))
And, if your business rules say your weeks start on Mondays, change the -1 to -2.
Edit
Years have gone by and I've finally gotten around to writing this up.
https://www.plumislandmedia.net/mysql/sql-reporting-time-intervals/
The accepted answer above did not work for me, because it ordered the weeks by alphabetical order, not chronological order:
2012/1
2012/10
2012/11
...
2012/19
2012/2
Here's my solution to count and group by week:
SELECT CONCAT(YEAR(date), '/', WEEK(date)) AS week_name,
YEAR(date), WEEK(date), COUNT(*)
FROM column_name
GROUP BY week_name
ORDER BY YEAR(DATE) ASC, WEEK(date) ASC
Generates:
YEAR/WEEK YEAR WEEK COUNT
2011/51 2011 51 15
2011/52 2011 52 14
2012/1 2012 1 20
2012/2 2012 2 14
2012/3 2012 3 19
2012/4 2012 4 19
You can get the concatenated year and week number (200945) using the YEARWEEK() function. If I understand your goal correctly, that should enable you to group your multi-year data.
If you need the actual timestamp for the start of the week, it's less nice:
DATE_SUB( field, INTERVAL DAYOFWEEK( field ) - 1 DAY )
For monthly ordering, you might consider the LAST_DAY() function - sort would be by last day of the month, but that should be equivalent to sorting by first day of the month ... shouldn't it?
Just ad this in the select :
DATE_FORMAT($yourDate, \'%X %V\') as week
And
group_by(week);
If you need the "week ending" date this will work as well. This will count the number of records for each week. Example: If three work orders were created between (inclusive) 1/2/2010 and 1/8/2010 and 5 were created between (inclusive) 1/9/2010 and 1/16/2010 this would return:
3 1/8/2010
5 1/16/2010
I had to use the extra DATE() function to truncate my datetime field.
SELECT COUNT(*), DATE_ADD( DATE(wo.date_created), INTERVAL (7 - DAYOFWEEK( wo.date_created )) DAY) week_ending
FROM work_order wo
GROUP BY week_ending;
Previous Sunday:
STR_TO_DATE(CONCAT(YEARWEEK(timestamp,2),'0'),'%X%V%w')
Previous Monday:
STR_TO_DATE(CONCAT(YEARWEEK(timestamp,3),'1'),'%x%v%w')
DATE_FORMAT(date,format) reference:
%V - Week (01..53), where Sunday is the first day of the week; WEEK() mode 2; used with %X
%v - Week (01..53), where Monday is the first day of the week; WEEK() mode 3; used with %x
%w - Day of the week (0=Sunday..6=Saturday)
%X - Year for the week where Sunday is the first day of the week, numeric, four digits; used with %V
%x - Year for the week, where Monday is the first day of the week, numeric, four digits; used with %v
I like the week function in MySQL, but in my situation, I wanted to know which week of the month a row was in. I utlized this solution:
where run_date is a timestamp like 2021-02-25 00:00:00
concat (
date_format(run_date, '%Y-%m'),
' wk ',
(week(run_date,1) - ( week(date_format(run_date, '%Y-%m-01')) - 1))
) as formatted_date
This outputs:
2021-02-23 ---> 2021-02 wk 4
2021-02-25 ---> 2021-02 wk 4
2021-02-11 ---> 2021-02 wk 2
2021-03-02 ---> 2021-03 wk 1
The idea behind this is that I want to know (with relative certainty) which week of the month in question did the date occur?
So we concatenate:
date_format(run_date, '%Y-%m') to get 2021-02
then we add the literal text string wk
then we use:
week(run_date, 1) to get the week (1 to start Monday) of this record, (which would be 7 because 02/21/2021 is in the 7th week of the year, and we subtract whatever the week is on the 1st day of this same month - the week() for 2021-02-01 is 5, because it is in the 5th week of the year:
(week(date_format(run_date, '%Y-%m-01'))
Unfortunately, this will start out the counting at 0, which people don't like, so we subtract 1 from the last part of the concatenation result so that the "week" start at 1.
This may be a good option:
SELECT
year(datetime_field) as year_date, week(datetime_field) as week_date
FROM
bd.table
GROUP BY
year_date, week_date;
It would look like this:
'2020', '14'
'2020', '15'
'2020', '16'
'2020', '17'
'2020', '18'