Selecting rows with a set date+a month offset with MySQL - 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.

Related

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.

Rewritten mySQL Statement

SELECT DATE(STR_TO_DATE(CONCAT(CONCAT(YEAR('$uDate1'), week), ' Monday'), '%X%V %W') +
INTERVAL (7 - DAYOFWEEK(STR_TO_DATE(CONCAT(CONCAT(YEAR('$uDate1'), week), ' Monday'),
'%X%V %W'))) DAY)
as week_end_date
What this statement does is take the date I give it ($uDate1) and give me the week end date (Saturday) of that week. This works well and I am happy with it, kinda.
I was wondering if there were some things I missed that would either make this more efficient or even if I missed some shortcuts to this.
Any suggestions for me?
week >= WEEK('$uDate1') AND week <= WEEK('$uDate2')
This is in my WHERE clause. So basically if I use this...
DATE('$uDate1', INTERVAL 7 - DAYOFWEEK('$uDate1') DAY)
...then it returns the same day for all records. I need it to be able to go over a span of a few weeks.
I have a column in my database named 'week'. It simply stores an INT that corresponds to the week of the year. (ex. 21 for this week)
I then have two date picker boxes. The output gets the week end date based of each week that is BETWEEN and INCLUDES the days chosen.
5/10/2016 & 5/26/2016 outputs 5/14/2016, 5/21/2016, 5/28/2016
What gets exported to CSV file looks something like this..
WEEK END, LAST NAME, FIRST NAME, ...
5/10/2016, Smith, John, ...
5/26/2016, Jones, James, ...
It outputs anyone who had hours during the week, with the week end date.
SIDE NOTE: I do appreciate the comments and help. I don't want anyone to stress over this though! Just curious if better way. :)
I am not sure why your current SQL is so complicated.
You say it is just to take a date and give me the week end date (Saturday) of that week .
How you are doing this at the moment is:-
Yours is taking the year
Adding the week of the year (I assume - should be WEEK('$uDate1') I think)
Adding on the day as a string (so for example for today it would be 2016 21 Monday )
Changing that string back to a date a datetime value
Converting that datetime value back to a date.
Then taking the year again
Adding the week of the year again
Getting the day of the week of the resulting string. As you have concatenated Monday on to the date then the day of the week will always be 2.
Taking that resulting day of the week and subtracting it from 7. As the day of the week will always be 2 this will always result in 5
Adding on the day as a string (so for example for today it would be 2016 21 Monday ).
This value is then added on to the previously calculated date, taking the Monday date and adding 5 days.
My suggestion was to just use:-
DATE_ADD($uDate1, INTERVAL 7 - DAYOFWEEK($uDate1) DAY)
which is far simpler, and appears to cover your requirements.
EDIT
Looking at your edit you want a list of all the Saturdays for weeks all or partially between 2 passed dates.
If so I think the following will do it and hopefully be more efficient as there is no need to translate dates to and from string. Note it relies on your week table to add to the date, hence only copes with date ranges of up to that many weeks.
SELECT DATE_ADD(DATE_ADD('$uDate1', INTERVAL 7 - DAYOFWEEK('$uDate1') DAY), INTERVAL `week` WEEK) AS aDate
FROM `week`
HAVING aDate BETWEEN '$uDate1' AND DATE_ADD('$uDate2', INTERVAL 7 - DAYOFWEEK('$uDate2') DAY)
ORDER BY aDate
As I mentioned in comment you should move this transformation from mysql query to php code.
I see no reason to do this calculation on mysql side.
http://ideone.com/48zLvF
$week_day = intval(date('w',$uDate1));
if ($week_day<6) {
$end_of_week = $uDate1+(86400*(6-$week_day));
} else {
$end_of_week = $uDate1;
}

Efficient SELECT query to find records within a month

I have a MySQL DB table with multiple date type fields. I need to do different SELECT queries on this table but I am not sure which way is the best to find records from the same month.
I know I can do the following:
SELECT *
FROM table
WHERE MONTH(somedate) = 5
AND YEAR(somedate) = 2015
But I keep reading that isn't efficient and that I should go with using actual dates, i.e.
SELECT *
FROM table
WHERE somedate BETWEEN '2015-05-01' AND '2015-05-31'
However, all I would have is the month and the year as variables coming in from PHP. How do I easily and quickly calculate the last day of the month if I go with second option?
Don't calculate the last day of the month. Calculate the first day of the next month instead.
Your query can be like this
WHERE t.mydatetimecol >= '2015-05-01'
AND t.mydatetimecol < '2015-05-01' + INTERVAL 1 MONTH
Note that we're doing a less than comparison, not a "less than or equal to"... this is very convenient for comparing TIMESTAMP and DATETIME columns, which can include a time portion.
Note that a BETWEEN comparison is a "less than or equal to". To get a comparison equivalent to the query above, we'd need to do
WHERE t.mydatetimecol
BETWEEN '2015-05-01' AND '2015-05-01' + INTERVAL 1 MONTH + INTERVAL -1 SECOND
(This assumes that the resolution of DATETIME and TIMESTAMP is down to a second. In other databases, such as SQL Server, the resolution is finer than a second, so there we'd have the potential of missing a row with value of '2015-05-31 23:59:59.997'. We don't have a problem like that with the less than the first day of the next month comparison... < '2015-06-01'
No need to do the month or date math yourself, let MySQL do it for you. If you muck with adding 1 to the month, you have to handle the rollover from December to January, and increment the year. MySQL has all that already builtin.
date('t', strtotime("$year-$month-01")) will give days in the month

SELECT where date is within X months away regardless of stored year

I currently have a query that is getting the records where their deadline is less than 3 months away. The deadline is stored as a Date, but the year is not important as this record should flag up every year.
My query:
SELECT client_name
FROM client
WHERE MOD(DAYOFYEAR(deadline) - DAYOFYEAR(CURDATE()), +365) <= 90
AND DAYOFYEAR(deadline) > DAYOFYEAR(CURDATE())
Apologies if there’s errors in the syntax as I’m writing this from memory – but it does work.
It works up until a deadline is in the first quarter and the current date is in the final quarter then it no longer returns the record. How do I get around this?
So the query needs to return the records that have a deadline within 3 months of the current date. The Year in the deadline date should be ignored as this could be years ago, but it is the day and month of the deadline that is important.
Or is the problem the date I am storing? Should I update this each year?
Thanks
One approach is to use a conditional test like this:
WHERE CONCAT(YEAR(NOW()),DATE_FORMAT(d.deadline,'-%m-%d'))
+ INTERVAL CONCAT(YEAR(NOW()),DATE_FORMAT(d.deadline,'-%m-%d'))<NOW() YEAR
< NOW() + INTERVAL 3 MONTH
We can unpack that a little bit. On that first line, we're creating a "next due" deadline date, by taking the current year, and appending the month and day value from the deadline.
But there's a problem. Some of those "next due" deadline dates are in the past. So, to handle that problem (when the 3 month period "wraps" into the next year), we need to add a year to any "next due" deadline date that's before the current date.
Now, we can compare that to a date 3 months from now, to determine if the "next due" deadline date is in the next 3 months.
That's a bit complicated.
Here's a SQL Fiddle as a demonstration: http://sqlfiddle.com/#!2/c90e9/3.
For testing, NOW() is inconvenient because it always returns today's date. So, for testing, we replace all occurrences of NOW() with a user-defined variable #now, and set that to various dates, so we can appropriately test.
Here's the SQL statement I used for testing. The first expression is the conditional test we're planning on using in the WHERE clause. For testing, we want to return all the rows, and just see which rows get due_in_3mo flagged as TRUE (1) and which get flagged as FALSE (0).
The second expression in the SELECT list just the "next due" deadline date, same as used in the first expression.
The rest of the expressions are pretty self-explanatory... we also want to display the date 3 months in the future we're comparing to, and the original "deadline" date value.
SELECT
CONCAT(YEAR(#now),DATE_FORMAT(d.deadline,'-%m-%d'))
+ INTERVAL CONCAT(YEAR(#now),DATE_FORMAT(d.deadline,'-%m-%d'))<#now YEAR
< #now + INTERVAL 3 MONTH
AS `due_in_3mo`
, CONCAT(YEAR(#now),DATE_FORMAT(d.deadline,'-%m-%d'))
+ INTERVAL CONCAT(YEAR(#now),DATE_FORMAT(d.deadline,'-%m-%d'))<#now YEAR
AS `next_due`
, d.id
, d.deadline + INTERVAL 0 DAY AS `deadline`
, #now + INTERVAL 3 MONTH AS `now+3mo`
, #now + INTERVAL 0 DAY AS `now`
FROM d d
CROSS
JOIN (SELECT #now := '2015-11-01') i
ORDER BY d.id
Change the value assigned to #now in the inline view (aliased as i) to test with other date values.
(You may want to use DATE(NOW()) in place of NOW() so that times don't get mixed in, and you may want to subtract another day from that, that really just depends how you want to handle the edge case of a deadline with month and day the same as the current date. (i.e. do you want to handle that as "in the past" or not.)
To summarize the approach: generate the "next due" deadline date as a DATE value in the future, and compare to the a date 3 months from now.

Find data of a whole month in sql

I have this query where I provide to-date & from date.
SELECT *
FROM sales
WHERE date between to-date AND from-date;
Now I want to execute this query with following parameters
to-date = Oct-2015
some-other-date = Oct-2015
That is I want records of the whole month.
How would I do that in a query where I have to and from dates provided it will work for both scenarios where months can be same and different as well.
Update:
dataType for column date is date
You can find the first day of the month containing any given timestamp with an expression like this. For example by using the timestamp NOW(), this finds the first day of the present month.
(DATE(NOW() - INTERVAL DAYOFMONTH(DATE(NOW()))
That's handy, because then you can use an expression like
(DATE(NOW() - INTERVAL DAYOFMONTH(DATE(NOW())) - INTERVAL 1 MONTH
to find the beginning of the previous month if you like. All sorts of date arithmetic become available.
Therefore, you can use an expression like the following to find all records with item_date in the month before the present month.
WHERE item_date>=(DATE(NOW()-INTERVAL DAYOFMONTH(DATE(NOW()))- INTERVAL 1 MONTH
AND item_date < (DATE(NOW()-INTERVAL DAYOFMONTH(DATE(NOW()))
Notice that we cast the end of a range of time as an inequality (<) to the moment just after then end of the range of time.
You may find this writeup useful. http://www.plumislandmedia.net/mysql/sql-reporting-time-intervals/
It's often useful to create a stored function called TRUNC_MONTH() to perform the conversion of the arbitrary timestamp to the first day of the month. It makes your SQL statements easier to read.
select * from sales
where from-date >= 1-Oct-2015
and to-date <= 1-Nov-2015
Update
select * from sales
where date >= from-date
and date <= to-date
Here is SQLFIDDLE
You Can get month from your both to and from dates and find records of that month
SELECT * FROM sales
WHERE MONTH('2002-01-03') AND MONTH('2002-01-3')
SqlFiddle of Using Month Function