Why are the YEARWEEKs of the both following dates different?
Both dates (2018-12-29 AND 2018-12-30) are in the same week?
SELECT YEARWEEK('2018-12-29 20:10:00'); = 201851
SELECT CURDATE(); = 2018-12-30
SELECT YEARWEEK(CURDATE()); = 201852
The default mode of operation of YEARWEEK and WEEK is set by the default_week_format system variable, which defaults to 0, in which mode weeks are assumed to start on Sunday. To do your computation based on weeks starting on Monday (so that 2018-12-29 and 2018-12-30 are in the same week), use one of the modes described in the manual which supports that (1, 3, 5 and 7). So for example
SELECT YEARWEEK('2018-12-29 20:10:00', 1), YEARWEEK('2018-12-30', 1)
Output:
201852 201852
Consider
SELECT YEARWEEK('2018-01-06'); returns 201753 -- saturday
SELECT YEARWEEK('2018-01-07'); returns 201801 -- sunday
while 2018-01-06 seems to return 201801.
returns the year and week number (a number from 0 to 53) for a given date, looks for whole(completed) weeks which end with each saturdays. Iterates to the next value at upcoming sundays
Related
I've managed to select the data from the current week but the week itself starts from Sunday which is not the right format for me, it should starts from Monday. I'm using MySQL to query the data.
SELECT IFNULL(SUM(rendeles_dbszam),0) as eladott_pizzak_szama
FROM rendeles
WHERE WEEK(rendeles_idopont) = WEEK(CURRENT_DATE())
'Week' in mysql has 2 inputs: date and week type. By default it's equal 0. That means week starts from sunday. Try this code:
SELECT IFNULL(SUM(rendeles_dbszam),0) as eladott_pizzak_szama FROM rendeles WHERE WEEK(rendeles_idopont) = WEEK(CURRENT_DATE(),1)
You can use this little formula to get the Monday starting the week of any given DATE, DATETIME, or TIMESTAMP object.
FROM_DAYS(TO_DAYS(datestamp) -MOD(TO_DAYS(datestamp) -2, 7))
I like to use it in a stored function named TRUNC_MONDAY(datestamp) defined like this.
DELIMITER $$
DROP FUNCTION IF EXISTS TRUNC_MONDAY$$
CREATE
FUNCTION TRUNC_MONDAY(datestamp DATETIME)
RETURNS DATE DETERMINISTIC NO SQL
COMMENT 'preceding Monday'
RETURN FROM_DAYS(TO_DAYS(datestamp) -MOD(TO_DAYS(datestamp) -2, 7))$$
DELIMITER ;
Then you can do stuff like this
SELECT IFNULL(SUM(rendeles_dbszam),0) as eladott_pizzak_szama
FROM rendeles
WHERE TRUNC_MONDAY(rendeles_idopont) = TRUNC_MONDAY(CURRENT_DATE())
or even this to get a report covering eight previous weeks and the current week.
SELECT SUM(rendeles_dbszam) as eladott_pizzak_szama,
TRUNC_MONDAY(rendeles_idopont) as week_beginning
FROM rendeles
WHERE rendeles_idopont >= TRUNC_MONDAY(CURDATE()) - INTERVAL 8 WEEK
AND rendeles_idopoint < TRUNC_MONDAY(CURDATE()) + INTERVAL 1 WEEK
GROUP BY TRUNC_MONDAY(rendeles_idopont)
I particularly like this TRUNC_MONDAY() approach because it works unambiguously even for calendar weeks that contain New Years' Days.
(If you want TRUNC_SUNDAY() change the -2 in the formula to -1.)
I'm trying to solve a task: I have a table containing information about ships' battles. Battle is made of name and date. The problem is to get the last friday of the month when the battle occurred.
WITH num(n) AS(
SELECT 0
UNION ALL
SELECT n+1 FROM num
WHERE n < 31),
dat AS (
SELECT DATEADD(dd, n, CAST(battles.date AS DATE)) AS day,
dateadd(dd, 0, cast(battles.date as date)) as fight,
name FROM num,battles)
SELECT name, fight, max(day) FROM dat WHERE DATENAME(dw, day) = 'friday'
I thought there must be a maximum of date or something, but my code is wrong.
The result should look like this:
Please, help!!
P.S. DATE_FORMAT is not available
Possible problem: as spencer7593 noticed - and as I should have done and didn't - your original query is not MySQL at all. If you're porting a query that's OK. Otherwise this answer will not be helpful, as it makes use of MySQL functions.
The day you want is number 4 (0 being Sunday in MySQL).
So you want the last day of the month if the last day of the month is a 4; if the day of the month is a 5 you want a date which is 1 day earlier; if the day of the month is a 3 you want a date which is 1 day later, but that's impossible (the month ends), so you really need a date six days earlier.
This means that if the daynumber difference is negative, you want it modulo seven.
You can then build this expression (#DATE is your date; I use a fake date for testing)
SET #DATE='2015-02-18';
DATE_SUB(LAST_DAY(#DATE), INTERVAL ((WEEKDAY(LAST_DAY(#DATE))+7-4))%7 DAY);
It takes the last day of the month (LASTDAY(#DATE)), then it computes its weekday, getting a number from 0 to 6. Adds seven to ensure positivity after subtracting; then subtract the desired daynumber, in this case 4 for Friday.
The result, modulo seven, is the difference (always positive) from the last day's daynumber to the wanted daynumber. Since DATE_SUB(date, 0) returns the argument date, we needn't use IF.
SET #DATE='1962-10-20';
SELECT DATE_SUB(LAST_DAY(#DATE), INTERVAL ((WEEKDAY(LAST_DAY(#DATE))+7-4))%7 DAY) AS friday;
+------------+
| friday |
+------------+
| 1962-10-26 |
+------------+
Your query then would become something like:
SELECT `name`, `date`,
DATE_SUB(LAST_DAY(`date`),
INTERVAL ((WEEKDAY(LAST_DAY(`date`))+7-4))%7 DAY) AS friday
FROM battles;
I'd like to get the number of a week from a DATE starting with Monday as the first day of the week. While WEEK() can partially accomplish this, I would like each week to be uniquely identified. I.e., rather than rolling over at 52 or 53 to 0 or 1, to continue counting to week 54, 55, etc.
What is the best way to accomplish this in SQL?
If the week numbers should be sequential (perhaps for calculating time spans), you can pick an arbitrary Sunday in the past that should be week 1, count how many days since that day, and divide by 7. (Choosing Sunday will make Monday the start of the week.)
SELECT CEIL( DATEDIFF( '2013-01-04', '1970-01-04' ) / 7 ) AS week; # 2244
If all you need is unique identification, you could use YEARWEEK() to get 201253, 201301 and so on.
SELECT YEARWEEK( '2013-01-04', 1 ) AS week; # 201301
I use the following solution, to make a unique string from month and a week
id=str(time.month)+str(time.isocalendar()[1])
I'd like to have the week number and year, for a given date, from MySQL with the following rules:
If the date is at the end of the year, but in the first week of the next year, I need to return 1 as the week number.
If the date is at the beginning of the year, but in the last week of the previous year, I need to return 52 (or 53) as the week number.
I've read the week function in MySQL but I can't get the result I want.
Date and Time Functions: WEEK(date[,mode])
I am on the french calendar, so I have to begin the week on Monday and week 1 is the first week with more than 3 days this year.
Therefore I can only use options 1 and 3.
When I write the following queries:
select week ('2012-12-31', 3), the result is 1
select week ('2012-12-31', 1), the result is 53
When I test on the 1st Jan 2016:
select week ('2016-1-1', 3), the result is 53
select week ('2016-1-1', 1), the result is 0
Option 1 can't be used, because I can't detect that 2012-12-31 is in the next year.
Option 3 can be used, but I have the add two pieces of logic: if weeknumber = 1 and month = 12, year + 1 and if weekumber = 53 and month = 1 then year - 1
Does someone have a better solution?
Regards
Ok, I think I get what you're trying to do now.
As the documentation says:
We decided to return 0 instead because we want the function to return
“the week number in the given year.”
If you want the week number for the year that the week is in, they suggest using the YEARWEEK() function, which takes the same mode arguments as WEEK():
If you would prefer the result to be evaluated with respect to the
year that contains the first day of the week for the given date...
use the YEARWEEK() function:
mysql> SELECT YEARWEEK('2000-01-01');
-> 199952
mysql> SELECT MID(YEARWEEK('2000-01-01'),5,2);
-> '52'
So some examples of what you'd use:
mysql> SELECT MID(YEARWEEK('2012-1-1',3),5,2)
-> '52'
mysql> SELECT MID(YEARWEEK('2012-12-31',3),5,2)
-> '01'
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'