Query that accounts for changes in the hour of a timestamp - mysql

The problem is as follows. I have a table that contains the following columns:
machine
timestamp
speed (meters/minute)
C1
22/9/2020, 16:45
15
C1
22/9/2020, 16:55
5
C1
22/9/2020, 17:20
19
What I want to know is the distance travelled in each hour for each machine, so I need to subtract the timestamp of the current row from the timestamp of the next row, and then multiply by the speed of the first row (e.g: 16:55 - 16:45 = 10 minutes -> 10 * 15 = 150 meters between 16:45 and 16:55).
I was able to do that by using a logic similar to the one below (it is not all the same query):
' 1st get the timestamp of the next row'
lead(query."timestamp") OVER (PARTITION BY query.id ORDER BY query."timestamp") AS lead_timestamp
' 2nd get the duration'
query."lead_timestamp" - query."timestamp" AS duration
' 3rd calculate the distance'
query."duration" * query."speed" AS distance
' 4th group by hour'
GROUP BY date_trunc('hour', CAST(query."timestamp" AS timestamp)
It works almost 100% fine. I get a table similar to the one below:
machine
timestamp
duration (meters)
C1
22/9/2020, 16:00
275
C1
22/9/2020, 17:00
...
But as you can see, as I group data per hour, the total meters for the hour 16:00 are not correct because there wasn't a timestamp after the timestamp equal to "22/9/2020, 16:55" that forced the grouping to end at "22/9/2020, 16:59". So, in the end, I added to the hour 16:00 part of the duration for the hour 17:00 (those 20 minutes were added to hour 16:00).
I am not sure how to solve this problem but I have looked into UNION to add an 'artificial' row whenever there's a transition of hour between timestamps, before even starting subtracting values to calculate the duration. But it seems rather complicated since I would have to do it for each machine and I don't know how many rows it would be.
Can you help me? Thanks! If I was not clear pls ask for more info!

This could be of help:
WITH cte as (
select 'c1' as machine, '2020-09-22 16:46' as timestamp, 15 as speed
union all
select 'c1', '2020-09-22 16:56', 5
union all
select 'c1', '2020-09-22 17:20', 19)
select
machine,
timestamp,
speed,
lead(timestamp) over (partition by machine) nextTime,
TIMEDIFF( lead(timestamp) over (partition by machine), timestamp) diffTime,
minute(TIMEDIFF( lead(timestamp) over (partition by machine), timestamp))*speed meters
from cte;
output:
machine
timestamp
speed
nextTime
diffTime
meters
c1
2020-09-22 16:46
15
2020-09-22 16:56
00:10:00.000000
150
c1
2020-09-22 16:56
5
2020-09-22 17:20
00:24:00.000000
120
c1
2020-09-22 17:20
19

Related

How to group datetime into intervals of 3 hours in mysql

I have the follwoing table structure
start
count
2022-08-02 22:13:35
20
2022-08-03 04:27:20
10
2022-08-03 09:21:48
10
2022-08-03 14:25:48
10
2022-08-03 14:35:07
10
2022-08-03 15:16:09
10
2022-08-04 07:09:07
20
2022-08-04 10:35:45
10
2022-08-04 14:42:49
10
I want to group the start column into 3 hour intervals and sum the count
like follows
interval
count
01h-03h
400
03h-06h
78
...
...
...
....
20h-23h
100
23h-01h
64
I have the following query but am not sure how to proceed from here
select hour(start), sum(count) from `table`
GROUP BY hour(start)
To do this you need to be able to take any DATETIME value and truncate it to the most recent three-hour boundary. For example you need to take 2022-09-06 19:35:20 and convert it to 2022-09-06 18:00:00.
Do that with an expression like this:
DATE(start) + INTERVAL (HOUR(start) - MOD (HOUR(start), 3)) HOUR
This truncates the value to the nearest DATE(), then adds back the correct number of hours.
So a query might look like this:
SELECT DATE(start) + INTERVAL (HOUR(start) - MOD (HOUR(start), 3)) HOUR,
SUM(count)
FROM table
GROUP BY DATE(start) + INTERVAL (HOUR(start) - MOD (HOUR(start), 3)) HOUR
The trick to solving this problem of aggregating database rows over blocks of time is, generally, to come up with the appropriate way of truncating the DATETIME or TIMESTAMP values. Writeup here.
And if you want to aggregate by 3 hour intervals, with all days gathered into a single result of eight rows, do this.
SELECT HOUR(start) - MOD(HOUR(start, 3)),
SUM(count)
FROM table
GROUP BY HOUR(start) - MOD(HOUR(start, 3))
Again, you use an expression to truncate each value to the appropriate block of time.
select hr_range,sum(res.cnt)
from (
select hour(start) hr,
case when hour(start) between 0 and 3 then '00h-03h'
when hour(start) between 4 and 6 then '04h-06h'
when hour(start) between 7 and 9 then '07h-09h'
....
when hour(start) between 21 and 23 then '21h-23h'
end as hr_range,
sum(count) as cnt from `table`
GROUP BY hour(start)
)res
group by hr_range
I think this is one of the way to solve the issue

MySQL query or procedure to return table from values computed over multiple rows

I have a network created MYSQL table with following fields:
IP_SRC, IP_DST, BYTES_IN, BYTES_OUT, START_TIME, STOP_TIME
1.1.1.1 8.8.8.8 1080 540 1580684018 1580684100
8.8.4.4 1.1.1.1 2000 4000 1580597618 1580597800
The TIME values are epoch time ticks and each record is basically one TCP session.
I would like formulate a query (or procedure) to return a table with the following fields:
DayOfMonth, TotalOutBytes
1 12345
2 83747
3 2389
where DayOfMonth is the last 14 days or a range of last "n" days (or to keep the focus on the main problem assume the values are 1, 2, 3 of Feb 2020). The challenge is to grab all rows from the network table where STOP_TIME falls within the timeticks for DayOfMonth value for that row and sum up the BYTES_OUT to report as TotalOutBytes for that date.
I'm afraid I'm somewhat new to MYSQL and hopelessly lost.
Consider:
select
day(from_unixtime(stop_time)) dayOfMonth,
sum(bytes_out) TotalOutBytes
from mytable
where stop_time >= unix_timestamp(current_date - interval 14 day)
group by day(from_unixtime(stop_time))
Rationale:
the where clause uses unix_timestamp() to generate the unix timstamp corresponding to 14 days before the current date, which we can use to filter the table
from_unixtime() turns an epoch timestam pto a datetime value, then day() gives you the day number for that point in time
you can then aggregate with that value and sum bytes_out per group

MySQL - sum data by day intervals

I have such data in my table:
I need to calculate "Paid" field of UserID which reoccurs in 7 day intervals. In this example I will SUM(Paid) for UserID "01" because it occurs 2 times in 7 days interval.
I can calculate it programmaticaly, but only in such date intervals (2016-01-01 - 2016-01-07; 2016-01-07 - 2016-01-13; etc.).
Maybe there is some possibility to perform this calculation at MySQL level in any 7 day intervals? For example: 2016-01-01 - 2016-01-07; 2016-01-02 - 2016-01-08; 2016-10-10 - 2016-10-16; etc.
I believe the method WEEK() returns the week number based on the calendar year, meaning that for 2016: 1st Jan, 2nd Jan and 3rd Jan would return 0, but 4th Jan would return 1, which to my understanding does not fit the requirements.
I would suggest:
SELECT `UserID`, SUM(`Paid`) FROM `table` GROUP BY DATEDIFF(`Date`, (SELECT MIN(`Date`) FROM `table`)) DIV 7, `UserID`

Get average day or week values

I have statistical data like this:
time val1
1424166578 51
1424166877 55
1424167178 57
1424167477 57
time is a unix timestamp. There is one record every 5 minutes excluding nights and sundays. This continues over several weeks.
Now I want to get these values for an average day and an average week. The result should include values for every 5 minutes like normal but for average past days or weeks.
The result should look like this:
time val1
0 43.423
300 46.635
600 51.887
...
So time could be a timestamp with relative time since day or week start. Perhaps it is better to use DATETIME... not sure.
If I use GROUP BY FROM_UNIXTIME(time, '%Y%m%d') for example I get one value for the whole day. But I want all average values for all days.
You seem to be interested in grouping dates by five minute intervals instead of dates. This is fairly straightforward:
SELECT
HOUR(FROM_UNIXTIME(time)) AS HH,
(MINUTE(FROM_UNIXTIME(time)) DIV 5) * 5 AS MM,
AVG(val1) AS VAL
FROM your_table
WHERE time > UNIX_TIMESTAMP(CURRENT_TIMESTAMP - INTERVAL 7 DAY)
GROUP BY HH, MM
The following result will explain how date is clamped:
time FROM_UNIXTIME(time) HH MM
1424166578 2015-02-17 14:49:38 14 45
1424166877 2015-02-17 14:54:37 14 50
1424167178 2015-02-17 14:59:38 14 55
1424167477 2015-02-17 15:04:37 15 00
I would approach this as:
select date(from_unixtime(time)) as day, avg(val)
from table t
group by date(from_unixtime(time))
order by day;
Although you can use the format argument, I think of that more for converting the value to a string than to a date/time.

Select date where there is data in-between

I have todayRainSoFar column which is filled into db every minute, and i would like to find out the maximum duration of rain this month, ie when it started to rain and how long did it last.
for example it started to rain 2014-04-01 at 08:00 and it was raining without pause until 2014-04-05 10:00
i have trouble with combined queries.
// this will select rows when there was any rain this month
SELECT LogDateTime, TodayRainSoFar
FROM sibeniku_monthly
WHERE TodayRainSoFar > 0.0
AND DATE_FORMAT(LogDateTime, "%m.") = 04
now I am looking for something to count maximum duration of in-between rows (LogDateTime is datetime and range is 1 minute), smth like
AND MAX (COUNT BETWEEN rows)
The approach used below is to keep a counter of consecutive minutes of rain and then get the row which has maximum value of the counter (this would give you the maximum duration of consecutive rain). Counter will be reset to 0 if rain has ended.
SELECT DATE_SUB(LogDateTime, INTERVAL counter MINUTE) AS StartTime, LogDateTime AS EndTime FROM (
SELECT IF(TodayRainSoFar = 0, #i:=0, #i:=#i+1) AS `counter`,
LogDateTime, TodayRainSoFar
FROM
sibeniku_monthly, (SELECT #i:= 0) i
ORDER BY LogDateTime
) t
ORDER BY `counter` DESC
LIMIT 0,1
Working Demo: http://sqlfiddle.com/#!2/8578b8/8
Try using DATEDIFF in your script, here is a link: http://technet.microsoft.com/en-us/library/ms189794.aspx
This will give you everything you need to know about DATEDIFF.