I have two columns in a MySQL table:
DateOfService (datetime)
BirthDate (date)
I want to run a MySQL query that will provide date difference between these two fields in months.
How can I do this in a MySQL select query?
Thanks.
Have a look at the TIMESTAMPDIFF() function in MySQL.
What this allows you to do is pass in two TIMESTAMP or DATETIME values (or even DATE as MySQL will auto-convert) as well as the unit of time you want to base your difference on.
You can specify MONTH as the unit in the first parameter:
SELECT TIMESTAMPDIFF(MONTH, '2012-05-05', '2012-06-04')
-- 0
SELECT TIMESTAMPDIFF(MONTH, '2012-05-05', '2012-06-05')
-- 1
SELECT TIMESTAMPDIFF(MONTH, '2012-05-05', '2012-06-15')
-- 1
SELECT TIMESTAMPDIFF(MONTH, '2012-05-05', '2012-12-16')
-- 7
It basically gets the number of months elapsed from the first date in the parameter list. This solution accounts for the varying amount of days in each month (28,30,31) as well as leap years.
If you want decimal precision in the number of months elapsed, it's a little more complicated, but here is how you can do it:
SELECT
TIMESTAMPDIFF(MONTH, startdate, enddate) +
DATEDIFF(
enddate,
startdate + INTERVAL
TIMESTAMPDIFF(MONTH, startdate, enddate)
MONTH
) /
DATEDIFF(
startdate + INTERVAL
TIMESTAMPDIFF(MONTH, startdate, enddate) + 1
MONTH,
startdate + INTERVAL
TIMESTAMPDIFF(MONTH, startdate, enddate)
MONTH
)
Where startdate and enddate are your date parameters, whether it be from two date columns in a table or as input parameters from a script:
Examples:
With startdate = '2012-05-05' AND enddate = '2012-05-27':
-- Outputs: 0.7097
With startdate = '2012-05-05' AND enddate = '2012-06-13':
-- Outputs: 1.2667
With startdate = '2012-02-27' AND enddate = '2012-06-02':
-- Outputs: 3.1935
This could work:
SELECT 12 * (YEAR(DateOfService)
- YEAR(BirthDate))
+ (MONTH(DateOfService)
- MONTH(BirthDate)) AS months
FROM table
Try this:
SELECT DATEDIFF(DateOfService, BirthDate) / 30 as months FROM ...
TRY with
PERIOD_DIFF(P1,P2)
Returns the number of months between periods P1 and P2. P1 and P2 should be in the format YYMM or YYYYMM. Note that the period arguments P1 and P2 are not date values.
mysql> SELECT PERIOD_DIFF(200802,200703);
-> 11
TIMESTAMPDIFF(MONTH, Start_date, End_date)
Example:
SELECT TIMESTAMPDIFF(MONTH, BirthDate, DateOfService) AS Months FROM Table
Based on a summary of all answers and a Google search, I think there are four almost similar ways to write it:
1)
TIMESTAMPDIFF(MONTH, Start_date, End_date) AS Period
E.g.
TIMESTAMPDIFF(MONTH, MIN(r.rental_date), MAX(r.rental_date)) AS Period
2)
PERIOD_DIFF(date_format(now(), '%Y%m'), date_format(time, '%Y%m')) as months
Or
PERIOD_DIFF(date_format(End_date(), '%Y%m'), date_format(Start_date, '%Y%m')) as months
E.g.
PERIOD_DIFF(date_format(MAX(r.rental_date), '%Y%m'), date_format(MIN(r.rental_date), '%Y%m')) as months
3)
PERIOD_DIFF(EXTRACT(YEAR_MONTH FROM NOW()), EXTRACT(YEAR_MONTH FROM time)) AS months
OR
PERIOD_DIFF(EXTRACT(YEAR_MONTH FROM End_date()), EXTRACT(YEAR_MONTH FROM Start_date)) AS months
E.g.
PERIOD_DIFF(EXTRACT(YEAR_MONTH FROM MAX(r.rental_date)), EXTRACT(YEAR_MONTH FROM MIN(r.rental_date))) as Months
4)
PERIOD_DIFF(concat(year(d1),if(month(d1)<10,'0',''),month(d1)), concat(year(d2),if(month(d2)<10,'0',''),month(d2))) as Months**
E.g.
PERIOD_DIFF(
concat(year(MAX(r.rental_date)),if(month(MAX(r.rental_date))<10,'0',''),month(MAX(r.rental_date))),
concat(year(MIN(r.rental_date)),if(month(MIN(r.rental_date))<10,'0',''),month(MIN(r.rental_date)))
) as Months
The 'right' answer depends on exactly what you need. I like to round to the closest whole number.
Consider these examples:
1st January -> 31st January: It's 0 whole months, and almost 1 month long.
1st January -> 1st February? It's 1 whole month, and exactly 1 month long.
To get the number of whole months, use:
SELECT TIMESTAMPDIFF(MONTH, '2018-01-01', '2018-01-31'); => 0
SELECT TIMESTAMPDIFF(MONTH, '2018-01-01', '2018-02-01'); => 1
To get a rounded duration in months, you could use:
SELECT ROUND(TIMESTAMPDIFF(DAY, '2018-01-01', '2018-01-31')*12/365.24); => 1
SELECT ROUND(TIMESTAMPDIFF(DAY, '2018-01-01', '2018-01-31')*12/365.24); => 1
This is accurate to +/- 5 days and for ranges over 1000 years. Zane's answer is obviously more accurate, but it's too verbose for my liking.
Related
I am looking at getting certain data from our user sign-ups. We have data from 2013 up to now, but I need data from the last six months of how many users have signed up each month.
I don't know where to begin with starting the SQL query as what I have done so far is all wrong and doesn't come up with what I need.
I have a c_date (CURRENT_TIMESTAMP) column with the dates the user signed up.
All I have managed to do is get the data for a single month
SELECT
COUNT(c_date) AS total
FROM
accounts
WHERE
c_date BETWEEN '2017-05-01 00:00:00' AND '2017-05-31 23:59:59'
I am using MySQL
Depending on your SQL flavor, I would suggest using the current_date SQL keyword and the interval datatype:
where c_date >= current_date - interval '180 day'
I don't know what system you're using but here is how you might do it in SQL Server. This sums up the counts by month and year
select datepart(month, c_date) as month, datepart(year, c_date) as year, count(*)as total
from accounts
where c_date > dateadd(month, -6, getdate())
group by datepart(month, c_date), datepart(year, c_date)
order by month desc
You want to aggregate the data. Something like this:
SELECT YEAR(c_date) as yyyy, MONTH(c_date) as mm, COUNT(*) AS total
FROM accounts a
WHERE c_date >= (CURDATE() - INTERVAL (1 - DAY(CURDATE())) )- INTERVAL 6 MONTH
GROUP BY yyyy, mm;
The WHERE clause calculates the first day of the month starting six months ago.
I have a list of periods during a year, and they are the same every year. You can think of it as a Season. They have a startDate and a endDate.
Because there can be Seasons that leap each other, what I need to to is query all the matching Seasons given a date, no matter what year.
As an example:
Season1: from 1st of January to 10th of January
Season2: from 6th of January to 8th of January
Season3: from 11th of January to 20th of January
Given the date 7th of January, I'd need to retrieve the Season1 and Season2.
I've tried converting all dates to the same year, but It doesn't work when the Start Date of a season in "later" than the End Date (for example, there's a period starting on November and ending of February).
Thanks in advance for the help.
Edit, sample data:
StartDate EndDate SeasonId
2000-08-01 2000-08-31 4
2000-12-29 2000-01-02 3
2000-06-01 2000-07-30 3
2000-09-01 2000-09-30 3
2000-01-06 2000-01-08 3
2000-04-07 2000-04-17 3
2000-04-28 2000-05-01 3
2000-06-02 2000-06-05 3
2000-06-23 2000-06-25 3
2000-09-08 2000-09-11 3
2000-09-22 2000-09-25 3
2000-10-12 2000-10-15 3
2000-11-01 2000-11-05 3
2000-12-01 2000-12-10 3
2000-12-22 2000-12-26 3
2000-03-01 2000-05-31 2
2000-10-01 2000-10-31 2
2000-11-01 2000-02-28 1
And I'd need, for example, the season for the date 2000-02-08, and retrieve seasonId = 1, or the date 2000-10-13and retrive seasonId = 3, seasonId = 2
I would do it in 2 'options': (the following SQL assumes you already got rid of the year in the table, and left only month-date format. )
select ... from seasons s where
(s.startDate <= s.endDate and s.startDate <= #mydate and s.endDate >= #mydate) or
(s.startDate > s.endDate and s.startDate >= #mydate and s.endDate <= #mydate)
You could query like this for the Season1:
select * from myTable where (month(myDate) = 1 and DAY(myDate) between 1 and 10)
If you have a season in more than one month, like start date January 20th, and finish date Febrery 10th, you could query this way:
select * from myTable where (month(myDate) = 1 and DAY(myDate) >= 20) or (month(myDate) = 2 and DAY(myDate) <= 10)
UPDATED WITH YOUR UPDATE
It is a little bit tricky, but it should work...
select * from seasons_table
where cast(cast(day(myDate) as char) + '/' + cast(month(myDate) as char) + '/' + '2000' as date) between
cast(cast(day(StartDate) as char) + '/' + cast(month(StartDate) as char) + '/' + '2000' as date) and
cast(cast(day(EndDate) as char) + '/' + cast(month(EndDate) as char) + '/' + '2000' as date)
given tblSeason with columns Id, startdate, enddate and your date as #myDate you would query as
Select Id From tblSeason WHERE #myDate BETWEEN startdate AND enddate
would give list of Id's of the seasons that match.
if you can't work from that, please give more information in your examples as to the structure you are querying and the expected outcome.
*Edit to ignore the year part you could do similar to
Declare #myDate datetime = '2016-10-13'
SELECT [StartDate]
,[EndDate]
,[SeasonId]
FROM [dbo].[Table_1]
where DATEPART(dy, #myDate) >= DATEPART(dy,StartDate)
AND (DATEPART(dy,#myDate) =< DATEPART(dy,EndDate) OR DATEPART(dy,StartDate) > DATEPART(dy,EndDate))
Why are you including the year in the table? That seems strange.
In any case, you only care about the MM-DD format, so use date_format() to convert the values to strings:
select t.*
from t
where (start_date <= end_date and
date_format(#date, '%m-%d') >= date_format(start_date, '%m-%d') and
date_format(#date, '%m-%d') <= date_format(end_date, '%m-%d')
) or
(start_date > end_date and
date_format(#date, '%m-%d') <= date_format(start_date, '%m-%d') and
date_format(#date, '%m-%d') >= date_format(end_date, '%m-%d')
);
The strings are fine for comparison, because you are only looking at the month and day components of the date.
Given the nature of your problem, I would recommend that you store start_date and end_date in a non-date format, such as MM-DD.
I have a table with birthdays in them, formatted in YYYY-MM-DD. My aim is to return results if the birthday is within the next 7 days.
I need it to use only the month and day, because if it reads the year also, a birthday in 1993 is never going to be within the next 7 days. It also needs take month changes into consideration.
For example, if its the 28th of Feb, and a birthday in the table is on the 1st March, that would be within 7 days, but not within the same month.
SELECT * FROM user WHERE DATE_FORMAT(birthday, '%m-%d') >= DATE_FORMAT(NOW() - INTERVAL 7 DAY, '%m-%d')
This is where i'm at, at the moment, but I know i'm miles off.
You need to format both of your dates in the WHERE clause.
SELECT *
FROM user
WHERE DATE_FORMAT(birthday, '%m-%d') BETWEEN DATE_FORMAT(NOW(), '%m-%d') AND DATE_FORMAT(DATE_ADD(NOW(), INTERVAL 7 DAY), '%m-%d')
retrieve current year with YEAR(CURDATE(), make a date biy concatenating with month and day coming from birthday and remove 7 days, check if curdate is in the range :
select *
from user
where
Curdate() between date_sub(CONCAT(YEAR(CURDATE()),'-', date_format(birthday, '%m-%d')), interval 7 day)
and CONCAT(YEAR(CURDATE()),'-', date_format(birthday, '%m-%d')) ;
Take year part from today's date and concatenate it with the month and day part of the dob column value. And cast that string to date. Then use that as a sub-query and check whether thet new column day difference is between 0 and 7.
Query
SELECT t.* -- select except the `new_col`
FROM (SELECT *,
CAST((Concat(YEAR(NOW()), '-', DATE_FORMAT(dob, '%m-%d'))) AS DATE
) AS
`new_col`
FROM `user`)t
WHERE DATEDIFF(t.`new_col`, NOW()) BETWEEN 0 AND 7;
I have a table in my database with two columns: month (1-12) and year (yyyy).
I need to select records between two dates, for exemple
select * from Calendar a where SOMEDATE between STARTDATE and ENDDATE.
So the question is: how can I create the STARTDATE and the ENDDATE from this two columns I have?
...where SOMEDATE between
STR_TO_DATE(CONCAT_WS('-',STARTYEAR,STARTMONTH,1),'%Y-%m-%d')
and
DATE_SUB(
STR_TO_DATE(CONCAT_WS('-',ENDYEAR,ENDMONTH + 1,1),'%Y-%m-%d')
, INTERVAL DAY 1
)
Note that we convert both parts to type date, and use date_sub to subtract a single day from ENDMONTH + 1, since we don't know how many days there are in the relevant month.
You can use this solution to make date from the year and month fields-
SELECT MAKEDATE(year, 1) + INTERVAL month - 1 MONTH FROM calendar;
The apply this one to WHERE condition, e.g. -
SELECT * FROM Calendar a
WHERE
MAKEDATE(year, 1) + INTERVAL month - 1 MONTH BETWEEN
#STARDATE - INTERVAL EXTRACT(DAY FROM #STARDATE) - 1 DAY
AND
#ENDDATE
But I have to ask you about the STARTDATE and ENDDATE criterias. What to do if STARTDATE is '2012-09-20'? Should the query return records with month = 9?
I have a table with a column dedicated solely to DATETIME values (e.g. "2010-11-12 12:34:56"). I'm trying to figure out how to select all rows from the table where the month is the month after the current month, while also taking into account for year changes (that is, if the current month is December in 2010, I want to select all values that are dated January 2011).
I have a hard time figuring out how to do it. This is my current query for selecting all values for the remainder of the current month:
$query = "SELECT * FROM mytable WHERE uid = " . $_SESSION['uid'] . " AND EXTRACT(YEAR FROM NOW()) = EXTRACT(YEAR FROM date_time) AND EXTRACT(MONTH FROM NOW()) = EXTRACT(MONTH FROM date_time) AND EXTRACT(DAY FROM NOW()) < EXTRACT(DAY FROM date_time) ORDER BY date_time";
I tried using the guide at http://dev.mysql.com/doc/refman/5.0/en/date-calculations.html (CURDATE and MOD), but I couldn't get it to work. :(
Any help is very appreciated!
... WHERE YEAR(date_time) = YEAR(DATE_ADD(NOW(), INTERVAL 1 MONTH))
AND MONTH(date_time) = MONTH(DATE_ADD(NOW(), INTERVAL 1 MONTH))
This one would be faster because it can utilize available indexes on the datetime field:
... WHERE date_time BETWEEN
DATE_SUB(DATE_ADD(NOW(), INTERVAL 1 MONTH),INTERVAL (DAY(NOW())-1) DAY)
AND
DATE_SUB(DATE_ADD(NOW(), INTERVAL 2 MONTH),INTERVAL (DAY(NOW())) DAY)