Date range in mysql - mysql

I have a requirement where the data needs to be retreived starting from a particluar month but for a years period only. Example would be all data dated from 1st August 2019 till a year after, which would be 1st August 2020. The month value could vary, so in this example it is August but it could be May or September as well.
SELECT
*
FROM
Reports
WHERE
Fromdate BETWEEN '2019-08-01' AND DATE_ADD('2019-08-01', INTERVAL 365 DAY)
ORDER BY dateFrom ASC;
Here between will only pick up data for 2019 and 365 days after but 2019 is baked in. How should i put the selection criteria in the between clause so that it is dynamic? I am thinking along the lines of below where i would declare a variable for the month but i am not sure how to specify the 365 days from that month.
set #monthSelected = 8; -- August
SELECT
*
FROM
Reports
WHERE
month(Fromdate) = #monthSelected
and Fromdate BETWEEN '2019-08-01' AND DATE_ADD('2019-08-01', INTERVAL 365 DAY)
ORDER BY dateFrom ASC;

This is what i came up with,
set #curYear = 2019 ;-- concat("'",Year(now()));
set #monthSelected = '10';
set #dateF = concat(#curYear,'-',#monthSelected,'-01',"'") ;
SELECT
*
FROM
Reports
WHERE
Fromdate BETWEEN #dateF AND DATE_ADD(#dateF , INTERVAL 365 DAY);
A bit crude and a bit on the slow slide but it seems to be working. Am open to hear other suggestions.

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

Date Intervals Doesn't work

I am trying to get data by week, month and year.
I store date YYYY-MM-DD HH:MM:SS.
What I am doing is below;
Fetch one week old data;
query + AND WEEK(date) = WEEK(CURDATE())
Fetch a month old data;
query + AND MONTH(date) = MONTH(CURDATE())
The thing is I couldnt be able to get the data correct. For instance when I want to get week old data, I am gettin a year old one too.
Is there any other query that I could use? I have tried DATE(NOW()) - INTERVAL 30 DAY. It works but very slow.
Thanks!
I believe that the problem is that the WEEK function returns the week of the year. So, Jan 1st 2017 might be week 1 (also might be week 53 of the previous year depending on the day of the week and how MySQL handles it). But then, Jan 1st of 2016 is also week 1 - just for a different year.
Trying changing it to:
query + AND WEEK(date) = WEEK(CURDATE()) AND YEAR(date) = YEAR(CURDATE())
Also, if you're storing this as a string then definitely change it to a DATETIME
WHERE ...
AND date >= CURDATE() - INTERVAL 7 DAY
AND date < CURDATE()
Gives you the 7 days ending with yesterday. Use other techniques to get a particular month or week.
This technique is also much faster for large tables with a suitable index. Hiding date inside a function, such as WEEK() prevents the use of an index.

MySQL select records between day of the year and year

I have a table where I insert the year, month number, week number of the year, and day number of the year with each record.
I'm using those fields to filter records instead of full date because I have millions of records, and I want to increase the performance by comparing integers instead of dates.
I was able to select records between dates based the day number and year. The query works well if the years are the same, but once I change the year the query doesn't work because I'm using AND.
Table looks like this:
ID
Day
Week
Month
Year
Full Date
Here is the working query
SELECT COUNT(id) AS records_count
FROM table1
WHERE table1.day >= 176
AND table1.day <= 275
AND table1.year <= 2015
AND table1.year >= 2015
And this is the query that I need to adjust
SELECT COUNT(id) AS records_count
FROM table1
WHERE table1.day >= 275
AND table1.day <= 176
AND table1.year <= 2014
AND table1.year >= 2015
Your method of storing the dates is exactly wrong for what you want to do. The general form would be:
where (year = 2014 and day >= 275 or year > 2014) and
(year = 2015 and day <= 176 or year < 2015)
This will work for any pair of years, not just those that are one year apart.
Now, if you stored the dates normally, then you would simply do:
where date >= makedate(2014, 275) and date <= makedate(2015, 176)
What is really, really nice about this structure is that MySQL can use an index on date. That isn't possible with your query.
In general, a micro-optimization such as using integers instead of some other data type is not worth the effort in relational databases. Usually, the cost of reading the data is much more expensive than processing within a row. And, in fact, this example is a great example of it. Why try to increase the speed of comparisons when you can remove the need for them entirely using an index?
SELECT COUNT(id) AS records_count
FROM table1
WHERE (year = 2014 and day >= 275)
OR (year = 2015 and day <= 176)
And as baao commented - an indexed date column is super fast and easier to query.
Generalized solution (can be from year 2000 to 2020 for example)
SELECT COUNT(id) AS cnt
FROM tbl
WHERE ((year > under_min_year and year < above_max_year)
and ((year <> min_year and year <> max_year)
or (year = min_year and day > 265)
or (year = max_year and day < 300)))
The problem is that you want to count the records that have the year smaller or equal than 2014 AND the same year bigger or equal than 2015. There's no number smaller or equal than 2014 and in the same time bigger or equal than 2015.

Selecting rows with a set date+a month offset with MySQL

I have a MySQL database with one table that contains a data field and a "period" field, in months - int.
The idea is that the date indicates a due date to begin a project inside my company. And the "period" the period of time it is suppose to take to finish it, in months.
I need to select rows that will impact a given year. So if I am generating a report for 2014, I need to select the rows such: date+period is inside 2014.
It will be easy to do it inside the program, but I am looking for a way to do it in the query - if possible.
So basically I just need a way to sum dates and ints in a query, where the int is the number of months.
Any thoughts?
It's easy to do date arithmetic in MySQL and other RDMS systems. You need all the records in which the start date is not after the year in question OR the end date is not before the year in question. That is this expression:
NOT(YEAR(start_date) > 2014 OR YEAR(start_date + INTERVAL period MONTH) < 2014)
This logically reduces to
YEAR(start_date) <= 2014 AND YEAR(start_date + INTERVAL period MONTH) >= 2014
So this query will do it.
SELECT whatever, whatever
FROM project
WHERE YEAR(start_date) <= 2014
AND YEAR(start_date + INTERVAL period MONTH) >= 2014
AND (whatever other selection criteria you have)
This will give all projects that were active during 2014, including those that started before 2014 and those that will still be in progress at the end of that year.

How to check if date is in range regardless of the year

I'm wondering what would be the easiest way in MySQL to check if given date is in range regardless of the year.
In database table I have two DATE fields: start and finish stored in YYYY-mm-dd
if start = 2013-11-01 and finish = 2014-03-01 anything between 1st of November and 1st of March of any year should be accepted.
Valid dates:
2020-01-01 1980-02-28
Invalid dates:
2013-10-30 1968-07-30
There are almost certainly cleaner ways of doing it, however this should work:
((DAYOFYEAR(finish_date) > DAYOFYEAR(start_date)
AND (DAYOFYEAR(#date) >= DAYOFYEAR(start_date)
AND DAYOFYEAR(#date) <= DAYOFYEAR(finish_date)))
OR (DAYOFYEAR(finish_date) <= DAYOFYEAR(start_date)
AND (DAYOFYEAR(#date) >= DAYOFYEAR(start_date)
OR DAYOFYEAR(#date) <= DAYOFYEAR(finish_date))))
For a start date in Oct 2012 and end date in Nov 2020 this will return all dates in the Oct-Nov range. If in fact would want it to return all Dates when the range is greater than a year (and hence covers all dates of the year) you could add:
OR DATEDIFF(Day, start_date, finish_date) > 356
before the final bracket.
use DAYOFYEAR:
When the Start Date is earlier in the year than the Finished Date:
the tested Date should lye between Start Date and Finish Date (or on Start or Finish)
When the Finished Date is earlier in the year than the Start Date:
the tested Date should lye outside the Start Date and Finish Date (or on Start or Finish)
You can use some date extract function and then check your condition..
for example.
SELECT EXTRACT(MONTH FROM TIMESTAMP '2013-11-01 20:38:40');
this will give ouput start month as 11
SELECT EXTRACT(MONTH FROM TIMESTAMP '2014-03-01 20:38:40');
this will give ouput end month as 3
now you can check the condition from above two result..
SELECT * FROM tableWithDates t WHERE month(t.start) >= 11 AND month(t.finish) < 3
if you want the first of march it will go like this:
SELECT * FROM tableWithDates t WHERE month(t.start) >= 11 AND (month(t.finish) < 3 OR month(finish) <= 3 AND day(finish)<=1)
Depending on the size of the data you will run this at. You can get into performance problems, as MySQL can't use indexes of calculated columns.
If you run into this i suggest spitting the month AND/OR day into separate columns.
Edit:
Given an one parameter input as '2008-02-29'
SELECT * FROM tableWithDates t
WHERE
month(t.start) >= month('2008-02-29') AND day(t.start) >= day('2008-02-29')
AND month(t.finish) <= month('2008-02-29') AND day(t.finish) <= day('2008-02-29')