End of the year with Mysql - mysql

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'

Related

How to get week number and date of that week between two dates using MySQL

I am using MySQL And I have two dates "From date" and "To date", and based on these date i want to get week number and dates of that week between "To" and "From" Dates.
I have tried the following mysql query.
SELECT count(*) as count,
CONCAT(DATE_FORMAT(DATE_ADD(FROM_DAYS(TO_DAYS(FROM_UNIXTIME(`webform_submissions`.`submitted`)) -MOD(TO_DAYS(FROM_UNIXTIME(`webform_submissions`.`submitted`)) -1, 7)),INTERVAL -6 DAY),'%M %d'), ' - ' ,DATE_FORMAT(FROM_DAYS(TO_DAYS(FROM_UNIXTIME(`webform_submissions`.`submitted`)) -MOD(TO_DAYS(FROM_UNIXTIME(`webform_submissions`.`submitted`)) -1, 7)),'%M %d')) as date ,
CONCAT(YEAR(FROM_UNIXTIME(`webform_submissions`.`submitted`)), '/', WEEK(FROM_UNIXTIME(`webform_submissions`.`submitted`))) as week
FROM `webform_submissions`
where `webform_submissions`.`nid` = 121
AND DATE_FORMAT(FROM_UNIXTIME(`webform_submissions`.`submitted`), '%Y-%m-%d') between '2019-11-01' and '2019-12-03'
GROUP BY week
ORDER BY `webform_submissions`.`submitted` ASC
The following result is display according to above query.
But it seems that it gives wrong result because week number 43 lies between 21-27 Oct and i want to get result between between '2019-11-01' and '2019-12-03'.
Expected output should be like the screenshot. Because From date "2019-11-01" lies between Oct 28- Nov 03 (Week 44). so records should be start from 44 week number.
Any Idea how to get correct number of week and dates?
Here's a somewhat easier to read version of your query (using nested subqueries since MySQL 5.6 doesn't support CTEs) and using DATE_FORMAT with the %x/%v format to generate the week to match your expected result (October 28 is the start of week 44). Note I've added a MIN into the generation of date so that the query will still work in MySQL 5.7 with SQL mode ONLY_FULL_GROUP_BY.
SELECT COUNT(*) AS count,
CONCAT(DATE_FORMAT(MIN(startofweek), '%M %d'),
' - ',
DATE_FORMAT(MIN(startofweek) + INTERVAL 6 DAY, '%M %d')) AS date,
week
FROM (SELECT submitted - INTERVAL (dayofweek + 6) % 7 DAY AS startofweek,
week
FROM (SELECT nid,
DATE(FROM_UNIXTIME(submitted)) AS submitted,
DATE_FORMAT(FROM_UNIXTIME(submitted), '%w') AS dayofweek,
DATE_FORMAT(FROM_UNIXTIME(submitted), '%x/%v') AS week
FROM webform_submissions
WHERE nid = 121
AND DATE(FROM_UNIXTIME(submitted)) BETWEEN '2019-11-01' AND '2019-12-03'
) AS dates
) AS ws
GROUP BY week
Output (for my sample data)
count date week
3 October 28 - November 03 2019/44
4 November 04 - November 10 2019/45
Demo on dbfiddle
Try to set the second parameter of WEEK() according to your expected result.
With the second parameter you can set the mode with wich you specify wether the week starts with sunday or monday, the week numbers starts with 0 or 1 and the rule defining the first week of the year.
See https://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html#function_week

Yearweek returns different results

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

MySQL: need to calculate the last Friday of a month

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;

Nth weekday of the month

Pseudo code of what I'd like to do:
SELECT * FROM table WHERE NTH_DAY(DATE(table_date)) = NTH_DAY($input_date);
I want to determine what the nth weekday of the month is for a given date. For example, if given the input "2013-08-30" it should return 5, since it is the fifth occurrence of that weekday (Friday) in the month.
I've been reading through countless similar questions but the majority are looking for just the opposite, for example they want to find the date of the fifth Friday, whereas I want to determine what the nth number of a date is. Other questions appear to be what I'm after but they're in different programming languages I don't understand.
Can someone please explain if this is possible in a MySQL query and how to do it?
To find which "nth" a given day is is rather easy:
select (case when day(table_date) between 1 and 7 then 1
when day(table_date) between 8 and 14 then 2
when day(table_date) between 15 and 21 then 3
when day(table_date) between 22 and 28 then 4
else 5
end) as NthDay
You can also do this using "remainder" arithmetic:
select 1 + floor((day(table_date) - 1) / 7) ) as NthDay
I'm going to throw my two cents in here with a third option that gets it based on finding the 1st of each day, and then adding multiples of 7 to get to the final result.
Function
CREATE FUNCTION `ISNTHDAYINMONTH`(checkDate DATE, weekNumber INTEGER, dayOfWeek INTEGER, monthOfYear INTEGER) RETURNS INTEGER
NO SQL
DETERMINISTIC
BEGIN
DECLARE firstOfMonth DATE;
SET firstOfMonth = DATE_SUB(checkDate, INTERVAL DAYOFMONTH(checkDate) - 1 DAY); #Find the first day of the current month
IF DAYOFWEEK(checkDate) = dayOfWeek AND MONTH(checkDate) = monthOfYear #Make sure at least this matches
AND DAYOFMONTH(checkDate) = ((1 + (7 + dayOfWeek - DAYOFWEEK(firstOfMonth)) % 7) + 7 * (weekNumber - 1)) THEN #When the date matches the nth dayOfWeek day of the month
RETURN 1;
ELSE
RETURN 0; #Nope
END IF;
END
Use
SELECT ISNTHDAYINMONTH(datecol, weekNumber, dayOfWeek, monthOfYear);
Where weekNumber is 1 through 5, dayOfWeek is 1 through 7 (1 = Sun), and monthOfYear is 1 through 12.
Example
To check if datecol is the second Tuesday of April:
SELECT ISNTHDAYINMONTH(datecol, 2, 3, 4);
Calculations
Let's break it down. This line gets the first day of the month for the date that is passed in.
SET firstOfMonth = DATE_SUB(checkDate, INTERVAL DAYOFMONTH(checkDate) - 1 DAY); #Find the first day of the current month
This check ensures that the date has the correct day of the week, and correct month (just in case the user passed in a date that isn't even the right day of the week).
DAYOFWEEK(checkDate) = dayOfWeek AND MONTH(checkDate) = monthOfYear
Now for the big one:
DAYOFMONTH(checkDate) = ((1 + (7 + dayOfWeek - DAYOFWEEK(firstOfMonth)) % 7) + 7 * (weekNumber - 1))
To get the amount we need to add to 1 to get the date, we calculate dayOfWeek that we are looking at, minus the day of the week that the first day of the month falls on to get the offset mod 7 ((dayOfWeek - DayOfWeek(first)) % 7).
Note that because the first of the month could land on a day before or on the day we are looking at, we add an additional 7 and then mod by seven. This is needed because MySQL's Mod function does not properly compute mod. I.e. -1 % 7 should be 6, but MySQL returns -1. Adding 7 and taking the modulus ensures the result will always be correct.
To summarize:
NumberToAddToOneToGetDateOfFirstWeekDay = (7 + DayOfWeek - DayOfWeek(firstDayOfMonth)) %
Then we add one and how every many multiples of 7 are needed to get to the correct week.
select ceiling(DATEPART(day, GETDATE())/7.0)
This will always give you nth occurrence number of current day in a month.
For example, if the current day is Friday, and it occurred 3rd time this month. It will return 3.
If you replace GETDATE() with your Date variable or column name it will return the occurrence of the day on that date in the corresponding month.
This would give the number of the week (which is actually the same as if it is the 5th friday or 5th Monday)
SELECT WEEK(dateasd,5) -
WEEK(DATE_SUB(dateasd, INTERVAL DAYOFMONTH(dateasd)-1 DAY),5)
from test
Use Weekday() function to find which day it is
Fiddle at
http://www.sqlfiddle.com/#!2/7a8b6/2/0

Writing a function to get days in a range grouped by month and perform some simple calculations on them

I am trying to write a function in mySQL that takes two dates(startDate and endDate) as parameters. It then calculates the days in each month.
The database contains a targetRevenue table that has got the target revenue values for each month and year.
id month year targetRev
25 1 2012 1000.00
26 2 2012 5000.00
27 3 2012 8000.00
The function finds the revenue for a month based on the number of days in it and then returns the total.
Example : startDate : 2012-01-19 endDate : 2012-03-24
Function returns [ targetRev(19 days in Jan) + targetRev(29 days Feb) + targetRev(24days in March)]
I am new to writing functions in mysql , so a little bit of help to get me started would be very useful. Thanks in advance!
If instead of your month and year columns, you represented the month of each record in your targetRevenue table by a DATE column containing the first day of each month:
ALTER TABLE targetRevenue
ADD COLUMN first DATE;
UPDATE targetRevenue
SET first = STR_TO_DATE(CONCAT_WS('-', year, month, 1), '%Y-%c-%e');
ALTER TABLE targetRevenue
DROP COLUMN year,
DROP COLUMN month;
You could then obtain the total target revenue for your project (assuming it is inclusive of both start and end date) with:
-- calculate the summation of
SELECT SUM(CONVERT(
-- number of project days in month...
GREATEST(0,
-- ...is calculated as the difference between...
DATEDIFF(
-- ...the last day of the project in this month...
LEAST('2012-03-24', LAST_DAY(first)),
-- ...and the first day of the project in this month...
GREATEST('2012-01-19', first)
)
-- ...plus one because first and last project days were inclusive
+ 1
)
-- multiply by the target revenue for this month
* targetRev
-- divide by the number of days in the month
/ DAY(LAST_DAY(first)),
-- convert result to fixed-point format, to two d.p.
DECIMAL(11,2)
)) AS total
FROM targetRevenue
-- only perform for months in which the project was active
WHERE '2012-01-19' <= LAST_DAY(first) AND first <= '2012-03-24'
See it on sqlfiddle.
If you can't change the schema, you could replace references to first with the value to which that column was updated above.
For this you can use SUM() function like:
SELECT SUM(targetRev) from your_table
WHERE date_column BETWEEN your_startDate_column AND your_endDate_column;
you need not to calculate days of each month..
Use this query like this
SELECT SUM(targetRev), MONTH(date_column) as mo
from your_table
WHERE date_column BETWEEN your_startDate AND your_endDate
GROUP BY mo;
This will give the result for each month total revenue (use like this logic)
If it is two different years you can use like
concat(year(date_column),month(date_column)) as mo