Finding month difference and then remaining days after months - mysql - mysql

I need to find the difference between two dates to do some calculations based on the result.
Let's say column start_date is having value 1/Jan/2014 and column and end_date is having value 15/Mar/2014. The result I want is following format:
months | days_remain |
----------------------
2 15
I can find MONTH difference and also DAY difference between separately (as 2 Months & 74 days) using TIMESTAMPDIFF function. But how to find out the remaining 15 days ?

You can use DATEDIFF to see the difference between 2 dates
SELECT DATEDIFF('2006-04-01','2005-04-01');
http://lists.mysql.com/mysql/196414

Try This, I hope it will Work For You.
select DateDiff(d, datepart(month,[Start_Date]),datepart(month,End_Date)) as Months,
(30-day(end_date)) as Days_remain
from Sdate

This will work for sure.
Declare #StartDate datetime
Declare #EndDate datetime
Declare #years varchar(40)
Declare #months varchar(30)
Declare #days varchar(30)
set #StartDate ='2014/01/01'
set #EndDate = '2014/03/15'
select #years=datediff(year,#StartDate,#EndDate)
select #months=datediff(month,#StartDate,#EndDate)-(datediff(year,#StartDate,#EndDate)*12)
select #days=datepart(d,#EndDate)-datepart(d,#StartDate)
select #years +' years, ' +#months +' months, '+#days +' days' asYearMonthDay

I think this might be what you want. It does however return 14 days for remaining, but as Jaugar Chang pointed out in a comment that should be correct if as the difference between March 1st and March 15th is 14 days.
select
timestampdiff(
month,
start_date,
end_date
) as months,
datediff(
end_date,
timestampadd(
month,
timestampdiff(
month,
start_date,
end_date
)
,start_date
)
) as days_remain
from test;
Sample SQL Fiddle
Sample result:
| START_DATE | END_DATE | MONTHS | DAYS_REMAIN |
|------------------|----------------|--------|-------------|
| January, 01 2014 | March, 15 2014 | 2 | 14 |
| January, 10 2014 | March, 13 2014 | 2 | 3 |

Try this. See the sample data as well:
select
dt1, dt2,
trunc( months_between(dt2,dt1) ) mths,
dt2 - add_months( dt1, trunc(months_between(dt2,dt1)) ) days
from
(
select date '2012-01-01' dt1, date '2012-03-25' dt2 from dual union all
select date '2012-01-01' dt1, date '2013-01-01' dt2 from dual union all
select date '2012-01-01' dt1, date '2012-01-01' dt2 from dual union all
select date '2012-02-28' dt1, date '2012-03-01' dt2 from dual union all
select date '2013-02-28' dt1, date '2013-03-01' dt2 from dual union all
select date '2013-02-28' dt1, date '2013-04-01' dt2 from dual
) sample_data;
Hope it helps.

Related

Count Number of a Specific Day(s) Between Two Dates

I have a single line in MySQL table: volunteers
user_id | start_date | end_date
11122 | 2017-04-20 | 2018-02-17
How can I find how many times the 3rd day or 24th day of a month appears? (i.e. 2017-05-03, 2017-06-03, 2017-12-24, 2018-01-24) I'm trying to get to the following count:
Sample Output:
user_id | number_of_third_day | number_of_twenty_fourth_day
11122 | 10 | 10
I look at the documentation online to see if there is a way I can say (pseudo):
SELECT
day, COUNT(*)
FROM volunteers
WHERE day(between(start_date, end_date)) in (3,24)
I tried to create a calendar table to no avail, but I would try to get the days, GROUP BY day, and COUNT(*) times that day appears in the range
WITH calendar AS (
SELECT start_date AS date
FROM volunteers
UNION ALL
SELECT DATE_ADD(start_date, INTERVAL 1 DAY) as date
FROM volunteers
WHERE DATE_ADD(start_date, INTERVAL 1 DAY) <= end_date
)
SELECT date FROM calendar;
Thanks for any help!
This one is more optimized since I generate date range by months not days as other questions, so its faster
WITH RECURSIVE cte AS
(
SELECT user_id, DATE_FORMAT(start_date, '%Y-%m-03') as third_day,
DATE_FORMAT(start_date, '%Y-%m-24') as twenty_fourth_day,
start_date, end_date
FROM table1
UNION ALL
SELECT user_id,
DATE_FORMAT(third_day + INTERVAL 1 MONTH, '%Y-%m-03') as third_day,
DATE_FORMAT(twenty_fourth_day + INTERVAL 1 MONTH, '%Y-%m-24') as twenty_fourth_day,
start_date, end_date
FROM cte
WHERE third_day + INTERVAL 1 MONTH <= end_date
)
SELECT user_id,
SUM(CASE WHEN third_day BETWEEN start_date AND end_date THEN 1 ELSE 0 END) AS number_of_third_day,
SUM(CASE WHEN twenty_fourth_day BETWEEN start_date AND end_date THEN 1 ELSE 0 END) AS number_of_twenty_fourth_day
FROM cte
GROUP BY user_id;
Demo here
A dynamic approach is.
but creating the dateranges, takes a lot of time, so you should have a date table to get the dates
CREATE TABLE table1
(`user_id` int, `start_date` varchar(10), `end_date` varchar(10))
;
INSERT INTO table1
(`user_id`, `start_date`, `end_date`)
VALUES
(11122, '2017-04-20', '2018-02-17')
,(11123, '2019-04-20', '2020-02-17')
;
Records: 2 Duplicates: 0 Warnings: 0
WITH RECURSIVE cte AS (
SELECT
user_id,
`start_date` as date_run ,
`end_date`
FROM table1
UNION ALL
SELECT
user_id,
DATE_ADD(cte.date_run, INTERVAL 1 DAY),
end_date
FROM cte
WHERE DATE_ADD(date_run, INTERVAL 1 DAY) <= end_date
)SELECT user_id,
SUM(DAYOFMONTH(date_run) = 3) as day_3th,
SUM(DAYOFMONTH(date_run) = 24) as day_24th
FROM cte
GROUP BY user_id
user_id
day_3th
day_24th
11122
10
10
11123
10
10
fiddle
In last MySQL version you can use recursion:
-- get list of all dates in interval
with recursive dates(d) as (
select '2017-04-20'
union all
select date_add(d, interval 1 day) from dates where d < '2018-02-17'
) select
-- calculate
sum(day(d) = 10) days_10,
sum(day(d) = 24) days_24
from dates
-- filter 10 & 24 days
where day(d) = 10 or day(d) = 24;
https://sqlize.online/sql/mysql80/c00eb7de69d011a85502fa538d64d22c/
As long as you are looking for days that occur in every month (so not the 29th or beyond), this is just straightforward math. The number of whole calendar months between two dates (exclusive) is:
timestampdiff(month,start_date,end_date) - (day(start_date) <= day(end_date))
Then add one if the start month includes the target day and one if the end month includes it:
timestampdiff(month,start_date,end_date) - (day(start_date) <= day(end_date))
+ (day(start_date) <= 3) + (day(end_date) >= 3)

SQL query to extract the most recent part and anything that is 30 days older than that date.

I am very new to SQL and I need to write a query that selects data for a specific part. However, It should select only the part that is the most recent(given by date) and anything that is only 30 days prior to it. Please consider the table below:
PartID | Part_NAME | DATE
-----------------------------
1 AAA 6/16/2015
2 BBB 6/15/2015
3 AAA 6/11/2015
4 AAA 1/1/2008
I need a query that gives me:
PartID | Part_NAME | DATE
-----------------------------
1 AAA 6/16/2015
3 AAA 6/11/2015
I have tried:
select * from ( select * from sales_table where Part_NAME = 'AAA') where DATE BETWEEN (max(DATE) and (max(DATE)-30))
I have read some articles saying that I cannot use WHERE and functions like max() together and advised me to use group by or having but it didn't work for me as well. Thank you.
IF you want data from the last 30 days of the current day, you can do :
SELECT *
FROM sales_table
WHERE
[DATE] >= DATEADD(DAY, -30,GETDATE())
AND [DATE] <= GETDATE()
AND Part_NAME = 'AAA'
IF you want data from the last 30 days from the last date of sale of each Part_NAME (this will take the max recorded date of sale for each Part_NAME and get the last 30 days records of each one of them.)
SELECT *
FROM (
SELECT *,
MAX([DATE]) OVER(PARTITION BY Part_NAME ORDER BY PartID) AS RecentDate
FROM sales_table
) D
WHERE
[DATE] >= DATEADD(DAY, -30, RecentDate)
AND [DATE] <= RecentDate
AND Part_NAME = 'AAA'
You can accomplish by using datediff and getdate() and a subquery.
SELECT * FROM (
SELECT *,DATEDIFF(DD,[DATE],GETDATE()) AS DAYSBETWEEN FROM sales_table
) AS X
WHERE DAYSBETWEEN <= 30
If you want data from the last 30 days, it would be:
select st.*
from sales_table st join
(select top (1) st2.*
from sales_table st2
order by st2.date desc
) st2
on st2.part_name = st.part_name and
st.date >= dateadd(day, -30, cast(getdate() as date));

SQL: Query periods of time given date

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.

mysql query for grabbing multiple date ranges

I seem to be having a bit of trouble coming up a query to achieve what I want. I have a table like the following..
| Date(TIMESTAMP) | Count |
|---------------------|-------|
| 2016-02-01 01:00:00 | 52 |
| 2016-01-05 11:30:00 | 14 |
| 2016-02-01 04:20:00 | 36 |
| ... | ... |
The table has about 40,000 rows. What I would like to do is grab the totals for multiple date ranges so I end up with the following...
| Period | Total |
|------------|-------|
| All | 10245 |
| Past year | 1401 |
| Past month | 104 |
| Past week | 26 |
Currently I am running through a loop in my PHP script and doing an individual query for each date range I'm looking for. Actually there are about 10 queries I'm doing per loop to grab different stats but for the example I'm simplifying it. This takes forever and I am hoping there is a more elegant way to do this, however I've spent quite a bit of time now trying different things and researching and have gotten nowhere. I understand how to use CASE to group but not when a record may need to be in multiple bins. Any help?
Try this UNION query:
SELECT 'All', COUNT(*) AS Total FROM yourTable
UNION
SELECT 'Past year', COUNT(*) AS Total
FROM yourTable
WHERE DATE(TIMESTAMP) > DATE_ADD(NOW(), INTERVAL -1 YEAR)
UNION
SELECT 'Past month', COUNT(*) AS Total
FROM yourTable
WHERE DATE(TIMESTAMP) > DATE_ADD(NOW(), INTERVAL -1 MONTH)
UNION
SELECT 'Past week', COUNT(*) AS Total
FROM yourTable
WHERE DATE(TIMESTAMP) > DATE_ADD(NOW(), INTERVAL -1 WEEK)
1st. get known to function getting first date of year, first date of month and first date of week.
Then compose your sql using count and filter with first and last date of different period.
ref:
MySQL Select First Day of Year and Month
month
https://stackoverflow.com/a/19259159/1258492
week https://stackoverflow.com/a/11831133/1258492
select 'All' as period, count(1) from
tbl
union
select 'Past Year' as period, count(1) from
tbl
where timestamp between
MAKEDATE(year(now())-1,1) and
last_day(MAKEDATE(year(now())-1,1) + interval 11 month)
union
select 'Past Month' as period, count(1) from
tbl
where timestamp between
LAST_DAY(NOW() - INTERVAL 2 MONTH) + INTERVAL 1 DAY and
LAST_DAY(NOW() - INTERVAL 1 MONTH)
union
select 'Past Week' as period, count(1) from
tbl
where timestamp between
adddate(curdate(), INTERVAL 1-DAYOFWEEK(curdate())-7 DAY) and
adddate(curdate(), INTERVAL 7-DAYOFWEEK(curdate())-7 DAY) ;
You may use subqueries. Use one subquery per time breakdown like so:
SELECT everything, 'past year'
FROM
(
SELECT sum(c) AS 'everything'
FROM reports
) t1,
(
SELECT sum(c) AS 'past year'
FROM reports
WHERE d >= DATE_ADD(CURDATE(), INTERVAL -1 YEAR)
) t2

decrease/increase between date in mysql or oracle SQL

I've problem in query mysql
I want to find hour per date between 2 date divided by day
for example,
I have startdate 14/12/2014 09:00:00AM , and enddate 17/12/2014 03:30:00AM
I want to result like this
14/12/2014 - 15 hour
15/12/1014 - 24 hour
16/12/2014 - 24 hour
17/12/2014 - 3,5 hour
Is that possible in a MySQL or Oracle SQL query?
Finally, I have found the query for oracle sql...
THANKS FOR YOUR HELP
SELECT
TO_DATE (START_DATE + COLUMN_VALUE - 1, 'DD/MM/YYYY') AS THE_DATE,
CASE
WHEN TO_DATE (START_DATE, 'DD/MM/YYYY') = TO_DATE (END_DATE, 'DD/MM/YYYY')
THEN (END_DATE - START_DATE)*24
WHEN (START_DATE + COLUMN_VALUE - 1) = START_DATE
THEN (23 - TO_CHAR (START_DATE, 'HH24')) + ((60 - TO_CHAR (START_DATE, 'MI'))/60) + ((360 - TO_CHAR (START_DATE, 'SS'))/360)
WHEN TO_DATE (START_DATE + COLUMN_VALUE - 1, 'DD/MM/YYYY') <> TO_DATE (END_DATE, 'DD/MM/YYYY')
THEN 24
ELSE TO_CHAR (END_DATE, 'HH24') +
(TO_CHAR (END_DATE, 'MI')/60) +
(TO_CHAR (END_DATE, 'SS')/360) END
FROM MY_TABLE,
TABLE( CAST( MULTISET (
SELECT
LEVEL L
FROM DUAL
CONNECT BY LEVEL <= END_DATE - START_DATE + 1) AS SYS.ODCINUMBERLIST))
Use MySQL TIMESTAMPDIFF or Oracle EXTRACT function to calculate difference between two value fields of Datetime type (2nd and 3rd parameters). 1st parameter passed is your selected measurement to count the difference. This could be HOUR, MINUTE, SECOND, DAY etc.
SELECT
`startdate` AS `date`,
TIMESTAMPDIFF(
HOUR,
`startdate`,
`enddate`) AS `hours_delta`
FROM my_table
The result would be:
___date_______hours_delta____
14/12/2014________66________
15/12/1014________..._________
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE MY_TABLE ( ID, START_DATE, END_DATE ) AS
SELECT 1, TO_DATE( '14/12/2014 09:00:00AM', 'DD/MM/YYYY HH:MI:SSAM'), TO_DATE( '17/12/2014 03:30:00AM', 'DD/MM/YYYY HH:MI:SSAM') FROM DUAL
UNION ALL SELECT 2, TO_DATE( '20/12/2014 09:00:00AM', 'DD/MM/YYYY HH:MI:SSAM'), TO_DATE( '20/12/2014 03:30:00PM', 'DD/MM/YYYY HH:MI:SSAM') FROM DUAL
Query 1:
WITH Differences AS (
SELECT ID,
TRUNC( START_DATE ) AS Day,
LEAST(
TRUNC( START_DATE ) + INTERVAL '1' DAY,
END_DATE
) - START_DATE AS Diff
FROM MY_TABLE
UNION
SELECT ID,
COLUMN_VALUE,
CASE WHEN COLUMN_VALUE < TRUNC( END_DATE )
THEN 1
ELSE END_DATE - COLUMN_VALUE
END
FROM MY_TABLE,
TABLE( CAST( MULTISET (
SELECT TRUNC( START_DATE ) + LEVEL
FROM DUAL
WHERE TRUNC( START_DATE ) + LEVEL <= TRUNC( END_DATE )
CONNECT BY
TRUNC( START_DATE ) + LEVEL <= TRUNC( END_DATE )
) AS SYS.ODCIDATELIST ) )
)
SELECT ID,
TO_CHAR( Day, 'YYYY-MM-DD' ) AS Day,
TO_CHAR( TO_NUMBER( Diff * 24 ), '99D90' )
|| ' hour ' AS Hours
FROM DIFFERENCES
Results:
| ID | DAY | HOURS |
|----|------------|--------------|
| 1 | 2014-12-14 | 15.00 hour |
| 1 | 2014-12-15 | 24.00 hour |
| 1 | 2014-12-16 | 24.00 hour |
| 1 | 2014-12-17 | 3.50 hour |
| 2 | 2014-12-20 | 6.50 hour |