Count days of daterange in daterange - mysql

I want to count how many days of a start and end-date are inside another start and end-date. Basically: How many days of the date 01.06.2020 - 06.06.2020 are inside the date 03.06.2020 - 31.12.2020. In this case the answer would be 4. The 3rd, 4th, 5th and 6th of June 2020.
I know that I can use TIMESTAMPDIFF to do calculations between 2 dates, but I can't wrap my head around a simple solution to do it with 2 dates inside 2 dates.
I can't believe nobody ever asked that question, but I can't find any solution to this.
Simple table:
+-------------+--------------+
| Start_date | End_date |
+-------------+--------------+
| 2020-06-03 | 2020-12-31 |
| 2014-09-08 | 2015-09-07 |
| 2015-01-15 | 2015-02-01 |
+-------------+--------------+
I look for something along the lines of:
SELECT * FROM available_dates WHERE TIMESTAMPDIFF(DAY,'2020-06-01','2020-06-06', Start_date, End_date) > 5

You can do this by taking the intersection of the two ranges using GREATEST on the start dates and LEAST on the end dates. Intersecting ranges will return a positive number, non-intersecting a negative one. So we use GREATEST again to zero out negative values to get the overlap. For example:
CREATE TABLE test (
`Start_date` DATE,
`End_date` DATE
);
INSERT INTO test
(`Start_date`, `End_date`)
VALUES
('2020-06-03', '2020-12-31'),
('2014-09-08', '2015-09-07'),
('2015-01-15', '2015-02-01');
SET #start = '2015-01-20';
SET #end = '2015=02-04';
SELECT GREATEST(TIMESTAMPDIFF(DAY, GREATEST(Start_Date, #start), LEAST(End_Date, #end))+1, 0) AS overlap
FROM test;
Output:
overlap
0
16
13
Demo on dbfiddle

Related

MySQL query that will find all the entries with a datetime within the last hour of a month

I am trying to get all records in a MySQL database where a datetime column's value is within the final hour of any month (i.e. January 31st 23:00-23:59, February 28th 23:00-23:59) etc.
Is there a way to write a query without writing a bunch of "BETWEEN" statements?
Suppose I have the following table:
id | createtime
-------------------------
1 | 2018-01-30 23:37:11
2 | 2018-01-31 22:00:00
3 | 2018-01-31 23:37:11
4 | 2018-02-28 22:00:00
5 | 2018-02-28 23:33:00
I want a query that would select rows 3 and 5 (the last hour of January and February respectively).
Although it is not really performant for loads of data, there is an option to use different date/time functions to accomplish this:
You can test it with this simple line:
SET #mytime = "2018-09-30 22:59:59";
SELECT
#mytime,
LAST_DAY(#mytime),
HOUR(#mytime),
DAY(#mytime),
DAY(LAST_DAY(#mytime)),
IF(DAY(LAST_DAY(#mytime)) = DAY(#mytime) AND HOUR(#mytime) = 23, 1, 0) AS is_final_hour
;
Just play with the datetime variable that is set at the beginning to see different results.
After that you can use it in a query like so:
SELECT
t.mydatetime,
IF(DAY(LAST_DAY(t.mydatetime)) = DAY(t.mydatetime) AND HOUR(t.mydatetime) = 23, 1, 0) AS is_final_hour
FROM
mytable AS t
HAVING
is_final_hour = 1
;

SQL: How to calculate correctly with any DATE_TIME and CURRENT_TIMESTAMP?

I have created a table where I listed some spare parts of my company.
The Table looks like this:
ID | Name | install_date | last_changed | order_nr. | WearStatus
----------
1 | "ABC" | **2017-11-08 10:34:40** | NULL | 123456 | **50**<br>
2 | "DEF" | **2017-11-08 10:34:40** | NULL | 654321 | **10**<br>
.
.
I want to calculate the Future Date, where the spare Parts should be changed. For example:
I installed a Part on the 01. December and exactly 10 Days later on the 11. December this part has a Wear of 50%, then I know: It has 10 more Days left.
My calculation is according to the rule of three formula: Time = (100 * delta t) / Wear
I use the following SQL command:
SELECT ( (100*(CURRENT_TIMESTAMP - First_Usage) / Wear) + First_Usage) As 'Change_Date' FROM spare_parts
And get the following Results:
If you see there are some crazy DATE_TIMES like 2017-19-41-43-19-31.
So my Question is now: How to format this "Numbers" to a useful DATE_TIME Format like YYYY-MM-DD hh:mm:ss ?
kind regards Toby
Try these 2 approaches:
SELECT DATE(DATE_ADD(First_Usage, INTERVAL (100*(DATEDIFF(NOW(),First_Usage) / Wear)) day)) As 'Change_Date' FROM spare_parts
or
SELECT FROM_UNIXTIME( (100*(CURRENT_TIMESTAMP - UNIX_TIMESTAMP(First_Usage)) / Wear) + UNIX_TIMESTAMP(First_Usage))) As 'Change_Date' FROM spare_parts
MySQL olredy have date intervals functional. If in Wear is already days count, your variant is
SELECT DATE_ADD(First_Usage, INTERVAL Wear DAY) as 'Change_Date' FROM spare_parts;
https://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html#function_date-add

mysql, getting time period between two time's when date and time are in separated column

I need to query the info in MySql where I'm given two time strings, so I need to find anything in between.
the format the table looks like
id | date | hour | other | columns | that are not important
-----------------------------------------------------------
1 | 2016-04-11| 1 | asdsa......
2 | 2016-04-11| 2 | asdasdsadsadas...
.
.
.
n | 2016-04-12| 23 | sadasdsadsadasd
Say I have the time strings 2016-04-11 1 and 2016-04-12 23 and I need to find all info from 1 to n. I can separate the date and hour and do a query using BETWEEN...AND for the date, but I have no idea how to fit the time into the formula. Using another BETWEEN definitely won't work, so I definitely need to fit the statement somewhere else. I'm not sure how to proceed though.
WHERE ((`date` = fromDate AND `hour` > fromHour) OR `date` > fromDate)
AND ((`date` = toDate AND `hour` < toHour) OR `date` < toDate)

Graph per-day from ranges in MySQL

I am trying to make a graph that has a point for each day showing the number of horses present per-day.
This is example of data I have (MySQL)
horse_id | start_date | end_date |
1 | 2011-04-02 | 2011-04-03 |
2 | 2011-04-02 | NULL |
3 | 2011-04-04 | 2014-07-20 |
4 | 2012-05-11 | NULL
So a graph on that data should output one row per day starting on 2011-04-02 and ending on CURDATE, for each day it should return how many horses are registered.
I can't quite wrap my head around how I would do this, since I only have a start date and an end date for each item, and I want to know per-day how many was present on that day.
Right now, I do a loop and a SQL query per day, but that is - as you might have guesses - thousands of queries, and I was hoping it could be done smarter.
If a day between 2011-04-02 and now contains nothing, I still want it out but with a 0.
If possible I would like to avoid having a table with a row for each day containing a count.
I hope it makes sense, I am very stuck here.
What you should have, is a table containing just dates from at least the earliest date in your current table till the current date.
Then you can use this table to left join it something like this:
SELECT
dt.date,
COUNT(yt.horse_id)
FROM
dates_table dt
LEFT JOIN your_table yt ON dt.date BETWEEN yt.start_date AND COALESCE(end_date, CURDATE())
GROUP BY dt.date
Be sure to have a column of your_table in the COUNT() function, otherwise it counts the NULL values too.
The COALESCE() function returns the first of its parameter which isn't NULL, so if you don't have an end_date specified, the current date is taken instead.

get the SUM between two given dates

if i want to get the total_consumption over a range of dates, how would i do that?
I thought i could do:
SELECT id, SUM(consumption)
FROM consumption_info
WHERE date_time BETWEEN 2013-09-15 AND 2013-09-16
GROUP BY id;
however this returns: Empty set, 2 warnings(0.00 sec)
---------------------------------------
id | consumption | date_time |
=======================================|
1 | 5 | 2013-09-15 21:35:03 |
2 | 5 | 2013-09-15 24:35:03 |
3 | 7 | 2013-09-16 11:25:23 |
4 | 3 | 2013-09-16 20:15:23 |
----------------------------------------
any ideas what i'm doing wrong here?
thanks in advance
You're missing quotes around the date strings: the WHERE clause should actually be written as...
BETWEEN '2013-09-15' AND '2013-09-16'
The irony is that 2013-09-15 is a valid SQL expression - it means 2013 minus 09 minus 15. Obviously, there's no date lying in between the corresponding results; hence an empty set in return
Yet there might be another, more subtle error here: you probably should have used this clause...
BETWEEN '2013-09-15 00:00:00' AND '2013-09-16 23:59:59'
... instead. Without setting the time explicitly it'll be set to '00:00:00' on both dates (as DATETIME values are compared here).
While it's obviously ok for the starting date, it's not so for the ending one - unless, of course, exclusion of all the records for any time of that day but midnight is actually the desired outcome.
SELECT SUM(consumption)
FROM consumption_info
WHERE date_time >= 2013-09-15 AND date_time <= 2013-09-16;
or
SELECT SUM(consumption)
FROM consumption_info
WHERE date_time BETWEEN 2013-09-15 AND 2013-09-16;
Its better to use CAST when comparing the date function.
SELECT id, SUM(consumption)
FROM consumption_info
WHERE date_time
BETWEEN CAST('2013-09-15' AS DATETIME)
AND CAST('2013-09-16' AS DATETIME)
GROUP BY id;