mysql query to average values every hour - mysql

The following query grabs the temperature, outside temperature, ph and total suspended solids from my aquarium database and displays it in 1 table shown below. each of the values is stored in its own table and is being logged every minute.
I would like to change the query below to be able to get the average value for each hour instead of displaying the value stored in the database at x hour:00:00.
Please help
SELECT DATE_FORMAT(timeTable.minuteTime, '%Y-%m-%d %k:%i') time,
oT2.temperature temperature,
T2.temperature temp,
S2.solids solids,
P2.Ph Ph
FROM
(
SELECT minuteTime.minuteTime minuteTime,
( SELECT MAX(time) FROM outside_temperature WHERE time <= minuteTime.minuteTime AND time >= NOW() - INTERVAL 1 DAY) otempTime,
( SELECT MAX(time) FROM temperature1 WHERE time <= minuteTime.minuteTime AND time >= NOW() - INTERVAL 1 DAY) tempTime,
( SELECT MAX(time) FROM Ph WHERE time <= minuteTime.minuteTime AND time >= NOW() - INTERVAL 1 DAY) phTime,
( SELECT MAX(time) FROM solids WHERE time <= minuteTime.minuteTime AND time >= NOW() - INTERVAL 1 DAY) solidsTime
FROM
(
SELECT DATE(time) + INTERVAL (HOUR(time) DIV 2 * 2) HOUR minuteTime
FROM Ph
WHERE time >= NOW() - INTERVAL 1 DAY AND time <= NOW()
UNION SELECT DATE(time) + INTERVAL (HOUR(time) DIV 2 * 2) HOUR
FROM solids
WHERE time >= NOW() - INTERVAL 1 DAY AND time <= NOW()
UNION SELECT DATE(time) + INTERVAL (HOUR(time) DIV 2 * 2) HOUR
FROM outside_temperature
WHERE time >= NOW() - INTERVAL 1 DAY AND time <= NOW()
UNION SELECT DATE(time) + INTERVAL (HOUR(time) DIV 2 * 2) HOUR
FROM temperature1
WHERE time >= NOW() - INTERVAL 1 DAY AND time <= NOW()
GROUP BY 1
) minuteTime
) timeTable
LEFT JOIN outside_temperature oT2 ON oT2.time = timeTable.otempTime
LEFT JOIN temperature1 T2 ON T2.time = timeTable.tempTime
LEFT JOIN solids S2 ON S2.time = timeTable.solidsTime
LEFT JOIN Ph P2 ON P2.time = timeTable.phTime
ORDER BY minuteTime ASC
This is the result of the query
2013-04-03 22:00 27.12 26.06 139 7.54
2013-04-04 0:00 27.06 26 142 7.54
2013-04-04 2:00 26.94 26 142 7.5
2013-04-04 4:00 26.87 25.94 142 7.5
2013-04-04 6:00 26.75 25.87 141 7.58
2013-04-04 8:00 26.87 25.87 141 7.58
2013-04-04 10:00 26.87 25.87 141 7.58
2013-04-04 12:00 26.87 25.87 141 7.58
2013-04-04 14:00 26.69 25.87 144 7.54
2013-04-04 16:00 26.56 25.81 144 7.58
2013-04-04 18:00 26.5 25.75 144 7.61
2013-04-04 20:00 26.81 25.75 144 7.43

USE
SELECT DATE_FORMAT(timeTable.minuteTime, '%Y-%m-%d %k:%i') time,
AVG(oT2.temperature) temperature,
AVG(T2.temperature) temp,
AVG(S2.solids) solids,
AVG(P2.Ph) Ph
......./*YOUR QUERY
--AT THE LAST
GROUP BY DATE_FORMAT(timeTable.minuteTime, '%Y-%m-%d %k:%i')

Related

How to retrieve data from previous 4 weeks (mySQL)

I am trying to write a query to get the last 4 weeks (Mon-Sun) of data. I want every week of data to be stored with an individual and shared table.
every week data store based on name if same name repeated on single week amt should sum and if multiple name it should be show data individual, To see an example of what I am looking for, I have included the desired input and output below.
this is my table
date
amt
name
2022-04-29
5
a
2022-04-28
10
b
2022-04-25
11
a
2022-04-23
15
b
2022-04-21
20
b
2022-04-16
20
a
2022-04-11
10
a
2022-04-10
5
b
2022-04-05
5
b
i want output like this
date
sum(amt)
name
2022-04-25 to 2020-04-29
16
a
2022-04-25 to 2020-04-29
10
b
2022-04-18 to 2022-04-24
35
b
2022-04-11 to 2022-04-17
30
a
2022-04-04 to 2022-04-10
10
b
I would appreciate any pointers or 'best-practises' which I should employ to achieve this task.
You can try to use DATE_ADD with WEEKDAY get week first day and end day.
SELECT
CASE WHEN
weekofyear(`date`) = weekofyear(NOW())
THEN 'current week'
ELSE
CONCAT(date_format(DATE_ADD(`date`, interval - WEEKDAY(`date`) day), '%Y-%m-%d'),' to ',date_format(DATE_ADD(DATE_ADD(`date`, interval -WEEKDAY(`date`) day), interval 6 day), '%Y-%m-%d'))
END 'date',
SUM(amt)
FROM T
GROUP BY
CASE WHEN
weekofyear(`date`) = weekofyear(NOW())
THEN 'current week'
ELSE
CONCAT(date_format(DATE_ADD(`date`, interval - WEEKDAY(`date`) day), '%Y-%m-%d'),' to ',date_format(DATE_ADD(DATE_ADD(`date`, interval -WEEKDAY(`date`) day), interval 6 day), '%Y-%m-%d'))
END
sqlfiddle
EDIT
I saw you edit your question, you can just add name in group by
SELECT
CONCAT(date_format(DATE_ADD(`date`, interval - WEEKDAY(`date`) day), '%Y-%m-%d'),' to ',date_format(DATE_ADD(DATE_ADD(`date`, interval -WEEKDAY(`date`) day), interval 6 day), '%Y-%m-%d')) 'date',
SUM(amt),
name
FROM T
GROUP BY
CONCAT(date_format(DATE_ADD(`date`, interval - WEEKDAY(`date`) day), '%Y-%m-%d'),' to ',date_format(DATE_ADD(DATE_ADD(`date`, interval -WEEKDAY(`date`) day), interval 6 day), '%Y-%m-%d')),
name
ORDER BY 1 desc
sqlfiddle
This is in SQL Server, and just a mess about. Hopefully it can be of some help.
with cteWeekStarts
as
(
select
n,dateadd(week,-n,DATEADD(week, DATEDIFF(week, -1, getdate()), -1)) as START_DATE
from
(values (1),(2),(3),(4)) as t(n)
), cteStartDatesAndEndDates
as
(
select *,dateadd(day,-1,lead(c.start_date) over (order by c.n desc)) as END_DATE
from cteWeekStarts as c
)
,cteSalesSumByDate
as
(
select s.SalesDate,sum(s.salesvalue) as sum_amt from
tblSales as s
group by s.SalesDate
)
select c3.n as WeekNum,c3.START_DATE,isnull(c3.END_DATE,
dateadd(day,6,c3.start_date)) as END_DATE,
(select sum(c2.sum_amt) from cteSalesSumByDate as c2 where c2.SalesDate
between c3.START_DATE and c3.END_DATE) as AMT
from cteStartDatesAndEndDates as c3
order by c3.n desc

Select rows from 6 am to 6 am

Help create a query that will select records from the table for the past period from 6AM until 6AM, the table has a timestamp field.
I will explain, for example:
Now: 2019-06-15 04:44:42
Period: 2019-06-13 06:00:00 - 2019-06-14 06:00:00
Now: 2019-06-15 07:44:42
Period: 2019-06-14 06:00:00 - 2019-06-15 06:00:00
Now: 2019-06-16 01:44:42
Period: 2019-06-14 06:00:00 - 2019-06-15 06:00:00
I think I was not the easiest request:
SELECT * FROM `table` WHERE `timestamp`
BETWEEN
DATE_ADD(DATE(NOW() - INTERVAL '1 6' DAY_HOUR), INTERVAL 6 HOUR)
AND
DATE_ADD(DATE(NOW() - INTERVAL '0 6' DAY_HOUR), INTERVAL 6 HOUR);
How simpler?
You can get the upper range limit with:
date(now() - INTERVAL 6 HOUR) + INTERVAL 6 HOUR
The lower limit would be exactly one day (24 hours) earlier:
date(now() - INTERVAL 6 HOUR) + INTERVAL 6 HOUR - INTERVAL 1 day
So your query could be:
SELECT *
FROM `table` t
WHERE t.timestamp >= date(now() - INTERVAL 6 HOUR) + INTERVAL 6 HOUR - INTERVAL 1 day
AND t.timestamp < date(now() - INTERVAL 6 HOUR) + INTERVAL 6 HOUR
But to avoid code duplication I would rewrite it to:
SELECT t.*
FROM `table` t
CROSS JOIN (
SELECT date(now() - INTERVAL 6 HOUR) + INTERVAL 6 HOUR as upper_limit
) r -- r for "range"
WHERE t.timestamp >= r.upper_limit - INTERVAL 1 day
AND t.timestamp < r.upper_limit
You can though use <= r.upper_limit instead of < r.upper_limit to get the same result as with BETWEEN.
SELECT start, start +interval 24 hour end
FROM ( SELECT date(now())
- interval if(hour(now())>6, 1, 2) day
+ interval 6 hour
as start
) dates;

mysql query to join 3 queries in 1 table while averaging

I have 3 very large tables, with values getting logged every minute,
below is an extract of these tables.
I would like to get hourly averages for a period of 1 Day of these tables and join them with respect to time, please note there is a couple of seconds gap between log time for ph and temperature
Table PH (extract only, this table is very large with more than 130,000 values)
ID time Ph
72176 2013-04-06 03:29:34 7.58
72177 2013-04-06 03:30:34 7.58
72178 2013-04-06 03:31:34 7.54
72179 2013-04-06 03:32:34 7.58
72180 2013-04-06 03:33:34 7.58
72181 2013-04-06 03:34:34 7.58
72182 2013-04-06 03:35:34 7.54
72183 2013-04-06 03:36:34 7.58
72184 2013-04-06 03:37:34 7.54
72185 2013-04-06 03:38:34 7.58
72186 2013-04-06 03:39:34 7.58
Table temperature1 (extract only, this table is very large with more than 130,000 values)
ID time temperature
133312 2013-04-06 03:29:36 25.37
133313 2013-04-06 03:30:36 25.37
133314 2013-04-06 03:31:36 25.37
133315 2013-04-06 03:32:36 25.31
133316 2013-04-06 03:33:36 25.31
133317 2013-04-06 03:34:36 25.31
133318 2013-04-06 03:35:36 25.37
133319 2013-04-06 03:36:36 25.31
133320 2013-04-06 03:37:36 25.31
133321 2013-04-06 03:38:36 25.31
133322 2013-04-06 03:39:36 25.37
Table solids (extract only, this table is very large with more than 130,000 values)
ID time solids
123791 2013-04-06 03:29:49 140
123792 2013-04-06 03:30:49 140
123793 2013-04-06 03:31:49 143
123794 2013-04-06 03:32:49 140
123795 2013-04-06 03:33:49 140
123796 2013-04-06 03:34:49 140
123797 2013-04-06 03:35:49 140
123798 2013-04-06 03:36:49 143
123799 2013-04-06 03:37:49 140
123800 2013-04-06 03:38:49 140
123801 2013-04-06 03:39:49 140
I am currently getting hourly averages using the query below
SELECT DATE_FORMAT(x.time,'%Y-%m-%d %H:00:00')
, avg(x.solids) avg_solids
FROM solids x where time >= NOW() - INTERVAL 1 DAY
GROUP
BY DATE_FORMAT(x.time,'%Y-%m-%d %H:00:00');
how can I efficiently join (with respect to time) the results of the query above for each sensor (x3) to be displayed in 1 table
===============================
this query below gets the hourly values, but not sure how to tweek it to get averages per hour
SELECT DATE_FORMAT(timeTable.minuteTime, '%Y-%m-%d %k:%i') time,
(oT2.temperature) temperature,
(T2.temperature) temp,
(S2.solids) solids,
(P2.Ph) Ph
FROM
(
SELECT minuteTime.minuteTime minuteTime,
( SELECT MAX(time) FROM outside_temperature WHERE time <= minuteTime.minuteTime AND time >= NOW() - INTERVAL 1 DAY) otempTime,
( SELECT MAX(time) FROM temperature1 WHERE time <= minuteTime.minuteTime AND time >= NOW() - INTERVAL 1 DAY) tempTime,
( SELECT MAX(time) FROM Ph WHERE time <= minuteTime.minuteTime AND time >= NOW() - INTERVAL 1 DAY) phTime,
( SELECT MAX(time) FROM solids WHERE time <= minuteTime.minuteTime AND time >= NOW() - INTERVAL 1 DAY) solidsTime
FROM
(
SELECT DATE(time) + INTERVAL (HOUR(time) DIV 1 *1 ) HOUR minuteTime
FROM Ph
WHERE time >= NOW() - INTERVAL 1 DAY AND time <= NOW()
UNION SELECT DATE(time) + INTERVAL (HOUR(time) DIV 1 *1) HOUR
FROM solids
WHERE time >= NOW() - INTERVAL 1 DAY AND time <= NOW()
UNION SELECT DATE(time) + INTERVAL (HOUR(time) DIV 1 *1) HOUR
FROM outside_temperature
WHERE time >= NOW() - INTERVAL 1 DAY AND time <= NOW()
UNION SELECT DATE(time) + INTERVAL (HOUR(time) DIV 1 *1) HOUR
FROM temperature1
WHERE time >= NOW() - INTERVAL 1 DAY AND time <= NOW()
GROUP BY 1
) minuteTime
) timeTable
LEFT JOIN outside_temperature oT2 ON oT2.time = timeTable.otempTime
LEFT JOIN temperature1 T2 ON T2.time = timeTable.tempTime
LEFT JOIN solids S2 ON S2.time = timeTable.solidsTime
LEFT JOIN Ph P2 ON P2.time = timeTable.phTime
GROUP BY DATE_FORMAT(timeTable.minuteTime, '%Y-%m-%d %k:%i')
ORDER BY minuteTime ASC
Hour() seems that it would be a useful function for this, since you are only looking at a single day. Perhaps something like this would work for you:
SELECT * FROM
(SELECT HOUR(time) hour, avg(ph) AS avg_ph
FROM ph
WHERE time >= NOW() - INTERVAL 1 DAY
GROUP BY hour) p
JOIN
(SELECT HOUR(time) hour, avg(temperature) AS avg_temp
FROM temperature1
WHERE time >= NOW() - INTERVAL 1 DAY
GROUP BY hour) t ON t.hour = p.hour
JOIN
(SELECT HOUR(time) hour, avg(solids) AS avg_solids
FROM solids
WHERE time >= NOW() - INTERVAL 1 DAY
GROUP BY hour) s ON s.hour = p.hour;
Being that it is using inner joins, I'm making the assumption that there will always be at least one records in each table for the hour, but it seems like a reasonable assumption.

How to get unique values for several different time ranges WHERE exec_datetime >= NOW() - INTERVAL n DAY?

I can select the total amount of unique ip addresses between a single time range
SELECT COUNT(DISTINCT ip_address) as ip_addr, exec_datetime
FROM requests
WHERE exec_datetime >= NOW() - INTERVAL 1 DAY;
ip_addr exec_datetime
45 12/10/2012 5:21
How do I return a result set for the following clauses in a single query...
WHERE exec_datetime >= NOW() - INTERVAL 1 DAY;
WHERE exec_datetime >= NOW() - INTERVAL 2 DAY;
...
WHERE exec_datetime >= NOW() - INTERVAL 14 DAY;
...so that the result set would look like this?
ip_addr exec_datetime
45 11/26/2012 5:21
85 11/27/2012 5:21
130 11/28/2012 5:21
170 11/29/2012 5:21
... ...
I would use something like this:
SELECT
COUNT(DISTINCT requests.ip_address) ip_addr,
days.day
FROM
(SELECT now()-INTERVAL 1 DAY as day UNION
SELECT now()-INTERVAL 2 DAY UNION
...
SELECT now()-INTERVAL 14 DAY) days
INNER JOIN requests
on requests.exec_datetime >= days.day
GROUP BY
days.day

SQL Hourly Data

The query below retrieves weather data from a MySql database, and groups this data in to an hourly format.
select hour(datetime) AS hour
, avg(Temperature) as AVGT
from Database.minute
WHERE DATETIME
BETWEEN (CURDATE() + INTERVAL (SELECT hour(NOW())) hour - INTERVAL 23 hour)
AND ((CURDATE() + INTERVAL (SELECT hour(NOW())) hour))
group by hour
order by (CURDATE() + INTERVAL (SELECT hour(NOW())) hour - INTERVAL 23 hour)
Output is as follows:
hour AVGT
19 11.730
20 11.970
21 11.970
22 11.760
23 11.660
0 11.700
1 11.830
2 12.370
3 12.770
4 12.840
5 12.840
6 12.540
7 12.500
8 12.030
9 12.100
10 12.300
11 12.060
12 11.090
13 10.920
14 10.920
15 10.820
16 10.760
17 10.690
18 10.560
The time is now 18:15. All of the above output is correct apart from the data gathered for hour '18'. Instead of getting the average value between 18:00 and 18:15, it just outputs the average at time 18:00. ie. ignoring data between 18:01 and 18:14.
How can I modify the above query to include data in the current hour (18:00 to Now)?
Thanks
Why don't you simply try
SELECT Hour(datetime) AS hour,
Avg(temperature) AS AVGT
FROM DATABASE.minute
WHERE datetime BETWEEN ( Curdate() + INTERVAL (SELECT Hour(Now())) hour -
INTERVAL 23 hour ) AND Now()
GROUP BY hour
ORDER BY ( Curdate() + INTERVAL (SELECT Hour(Now())) hour - INTERVAL 23 hour )
I agree with #Ankur's answer (your filter citerion should not filter records up to the current hour, but rather the current time), however your date/time operations are very strange:
You don't need a subquery (SELECT Hour(NOW())) to obtain HOUR(NOW());
You can express ( Curdate() + INTERVAL (SELECT Hour(NOW())) hour - INTERVAL 23 hour ) more simply:
CURDATE() + INTERVAL HOUR(NOW()) - 23 HOUR
Or, in my view, more clearly:
DATE_FORMAT(NOW() - INTERVAL 23 HOUR, '%Y-%m-%d %H:00:00')
Your ORDER BY clause is a constant and therefore achieves nothing: did you mean to order by hour?
Therefore:
SELECT HOUR(datetime) AS hour,
AVG(Temperature) AS AVGT
FROM Database.minute
WHERE datetime BETWEEN
DATE_FORMAT(NOW() - INTERVAL 23 HOUR, '%Y-%m-%d %H:00:00')
AND NOW()
GROUP BY hour
ORDER BY hour