MySQL timestampdiff of records on same column - mysql

I have a table in which I am trying to sum times on the same column. I have a column where I log all time entries of any event. So, I would like to group them by gateway ID and have the value in hours, minutes and seconds. I have tried to used timestampdiff, but it takes three arguments and since I have only one row, I couldn't find a way. Is that possible? My table looks like this:
+----+---------+---------------------+
| id | gateway | timestamp |
+----+---------+---------------------+
| 1 | 1 | 2018-03-21 08:52:51 |
| 2 | 1 | 2018-03-21 08:52:54 |
| 3 | 1 | 2018-03-21 08:52:58 |
| 4 | 1 | 2018-03-21 08:53:11 |
| 5 | 2 | 2018-03-21 08:53:51 |
| 6 | 1 | 2018-03-21 08:54:21 |
| 7 | 2 | 2018-03-21 08:54:32 |
| 8 | 2 | 2018-03-21 08:54:44 |
| 9 | 2 | 2018-03-21 08:54:53 |
| 10 | 2 | 2018-03-21 08:55:01 |
+----+---------+---------------------+
basically I would like to group all records by their gateway and then have the sum of time there.
Thanks in advance!

I think you want:
select gateway,
timestampdiff(seconds, min(timestamp), max(timestamp))
from t
group by gateway;
I'm a little confused by your question, because timestampdiff() takes three arguments, not two.

Related

Get amount of records with specific value, but only once per unique field

I'm not looking for a complete answer, but maybe some pointers as to what kind of mysql commands I should look at to figure this out.
I have a series of sensors (30+) connected to my network. At different intervals I request their status and each of the devices replies with n-amount of booleans, where n can be anything from zero to 120 (so the response can be an empty object, a list of 120 booleans, or any amount in between).
Per received boolean I create a new record, together with the device's mac address and a timestamp. For example (see also this sqlfiddle):
+----+-------------------+---------------------+--------+
| id | device_address | timestamp | status |
+----+-------------------+---------------------+--------+
| 1 | f2:49:d2:17:5d:8d | 2018-09-22 15:54:51 | 0 |
| 2 | fd:30:ec:08:67:9a | 2018-09-22 15:54:56 | 0 |
| 3 | f8:8d:d9:64:a4:7c | 2018-09-22 15:54:58 | 1 |
| 4 | f2:49:d2:17:5d:8d | 2018-09-22 15:55:51 | 0 |
| 5 | f2:49:d2:17:5d:8d | 2018-09-22 15:55:52 | 0 |
| 6 | fd:30:ec:08:67:9a | 2018-09-22 15:55:56 | 1 |
| 7 | f8:8d:d9:64:a4:7c | 2018-09-22 15:55:58 | 1 |
| 8 | f2:49:d2:17:5d:8d | 2018-09-22 15:56:52 | 0 |
| 9 | f2:49:d2:17:5d:8d | 2018-09-22 15:57:52 | 1 |
| 10 | f2:49:d2:17:5d:8d | 2018-09-22 15:58:52 | 1 |
+----+-------------------+---------------------+--------+
Or, with the mac address replaced for better readability:
+----+-------------------+---------------------+--------+
| id | device_address | timestamp | status |
+----+-------------------+---------------------+--------+
| 1 | A | 2018-09-22 15:54:51 | 0 |
| 2 | BB | 2018-09-22 15:54:56 | 0 |
| 3 | CCC | 2018-09-22 15:54:58 | 1 |
| 4 | A | 2018-09-22 15:55:51 | 0 |
| 5 | A | 2018-09-22 15:55:52 | 0 |
| 6 | BB | 2018-09-22 15:55:56 | 1 |
| 7 | CCC | 2018-09-22 15:55:58 | 1 |
| 8 | A | 2018-09-22 15:56:52 | 0 |
| 9 | A | 2018-09-22 15:57:52 | 1 |
| 10 | A | 2018-09-22 15:58:52 | 1 |
+----+-------------------+---------------------+--------+
In the end I want to be able to graph these values, grouped in intervals. For example, when I graph the last 2 hours worth of data, I want to use 5 minute intervals. Per interval I want to know how many (unique) devices had a status of 1 at least once in that period, and how many only had zeroes. Devices that don't appear within the timeblock at all (because they didn't return a boolean) are not relevant to that timeblock
The above records would fall within two of such 5 minute timeblocks:
15:50:00 to 15:54:59 - ids 1 2 3
15:55:00 to 15:59:59 - ids 4 5 6 7 8 9 10
The kind of response I'd like is something like this:
+---------------------+---------------------------------+-------------------------+
| timeblock start | dev w/ at least one status of 1 | dev w/ only status of 0 |
+---------------------+---------------------------------+-------------------------+
| 2018-09-22 15:50:00 | 1 | 2 |
| 2018-09-22 15:55:00 | 2 | 1 |
+---------------------+---------------------------------+-------------------------+
The final result does not have to be like this exactly, other results that can help me deduce these numbers would also work. The same is true for the timestamp field; this 2018-09-22 15:50:00 format would be great but other formats can also allow me to deduct what the timeblock was.
Doing something like this gets me the different timeblocks and the amount of unique devices within each timeblock, but it counts the total amount of 1s and 0s instead of combining the results of each unique device.
SELECT timestamp,
SUM(status) as ones, COUNT(status)-SUM(status) as zeroes,
COUNT(DISTINCT(device_address)) as unique_devices
FROM records
GROUP BY UNIX_TIMESTAMP(timestamp) DIV 300
ORDER BY timestamp ASC
result:
+----------------------+------+--------+----------------+
| timestamp | ones | zeroes | unique devices |
+----------------------+------+--------+----------------+
| 2018-09-22T15:54:51Z | 1 | 2 | 3 |
| 2018-09-22T15:57:52Z | 4 | 3 | 3 |
+----------------------+------+--------+----------------+
Use conditional aggregation
SELECT timestamp,
count(distinct case when status = 1 then device_address end) as ones,
count(distinct case when status = 0 then device_address end) as zeros,
FROM records
GROUP BY UNIX_TIMESTAMP(timestamp) DIV 300
ORDER BY timestamp ASC
sqlfiddle demo

mysql - query to lookup multiple records

I have a single mysql table. Query should lookup prev_id multiple times till the insertimestamp is between cuurent and previous week and get the rows to columns.
ID | inserttimestamp| prev_id | category
-----------------------------
5 | 2017-06-08 | 4 | Level456
4 | 2017-06-05 | 3 | Level241
3 | 2017-05-31 | 2 | Level456
2 | 2017-05-28 | 1 | Level247
1 | 2017-05-27 | | Level231
The result should be something like this,
ID | inserttimestamp| prev_id | category | id1 | id1_category | id2 | id2_category |
--------------------------------------------------------------------------------------------
5 | 2017-06-08 | 4 | Level456 | 4 | Level 241 | 3 | Level456 |
In this case as you see it stopped to id 3 because id 2 and 1 are not falling under previous week.
Use Case: To find out how many level upgrade/downgrade happened since previous week
For the above records its Levelupgrade - 1 , LevelDowngrade -1
Any help would be highly appreciated. Thanks in advance.

MySQL - select average of column A for first N entries from column B

I have a ratings table, where each user can add one rating a day. But each user might miss several days between ratings.
I'd like to get the average rating for each user_id's first 7 entries of created_at.
My table:
mysql> desc entries;
+------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| rating | tinyint(4) | NO | | NULL | |
| user_id | int(10) unsigned | NO | MUL | NULL | |
| created_at | timestamp | YES | | NULL | |
+------------+------------------+------+-----+---------+----------------+
Ideally I'd just get something like:
+------------+------------------+
| day | average_rating |
+------------+------------------+
| 1 | 2.53 |
+------------+------------------+
| 2 | 4.30 |
+------------+------------------+
| 3 | 3.67 |
+------------+------------------+
| 4 | 5.50 |
+------------+------------------+
| 5 | 7.23 |
+------------+------------------+
| 6 | 6.98 |
+------------+------------------+
| 7 | 7.22 |
+------------+------------------+
The closest I've been able to get is:
SELECT rating, user_id, created_at FROM entries ORDER BY user_id asc, created at desc
Which isn't very close at all...
Is it even possible? Will the performance be terrible? It's something that would need to run every time a web page is loaded, so would it be better to just run this once a day and save the results? (to another table!?)
edit - second attempt
Working towards a solution, I think this would get the rating for each user's first day:
select rating from entries where user_id in
(select user_id from entries order by created_at limit 1);
But I get:
ERROR 1235 (42000): This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
So now I'm going to play around with JOIN to see if that helps.
edit - third attempt, getting closer
I found this stackoverflow post, which is closer to what I want.
select e1.* from entries e1 left join entries e2
on (e1.user_id = e2.user_id and e1.created_at > e2.created_at)
where e2.id is null;
It gets the rating for the first day for each user.
Next step is to work out how to get days 2 to 7. I can't use 1.created_at > e2.created_at for that, so I'm really confused now.
edit - fourth attempt
Okay, I think it's not possible. Once I worked out how to turn off 'full group by' mode, I realised I'll probably need to use a subquery with limit <user_id>, <day_num>, for which I get:
ERROR 1235 (42000): This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
My current method is to just get the entire table, and use PHP to calculate the average for each day.
If I understand correctly you want to take the last 7 ratings the user gave, ordered by the date they gave the rating. The last 7 ratings of one user may fall on different days to another user, however they will be averaged together regardless of date.
First we need to order the data by user and date and give each user their own incrementing row count. I do this by adding two variables, one for the last user id and one for the row number:
select e.created_at,
e.rating,
if(#lastUser=user_id,#row := #row+1, #row:=1) as row,
#lastUser:= e.user_id as user_id
from entries e,
( select #row := 0, #lastUser := 0 ) vars
order by e.user_id asc,
e.created_at desc;
If the previous user_id is different we reset the row counter to 1. The result from this is:
+---------------------+--------+------+---------+
| created_at | rating | row | user_id |
+---------------------+--------+------+---------+
| 2017-01-10 00:00:00 | 1 | 1 | 1 |
| 2017-01-09 00:00:00 | 1 | 2 | 1 |
| 2017-01-08 00:00:00 | 1 | 3 | 1 |
| 2017-01-07 00:00:00 | 1 | 4 | 1 |
| 2017-01-06 00:00:00 | 1 | 5 | 1 |
| 2017-01-05 00:00:00 | 1 | 6 | 1 |
| 2017-01-04 00:00:00 | 1 | 7 | 1 |
| 2017-01-03 00:00:00 | 1 | 8 | 1 |
| 2017-01-02 00:00:00 | 1 | 9 | 1 |
| 2017-01-01 00:00:00 | 1 | 10 | 1 |
| 2017-01-13 00:00:00 | 1 | 1 | 2 |
| 2017-01-11 00:00:00 | 1 | 2 | 2 |
| 2017-01-09 00:00:00 | 1 | 3 | 2 |
| 2017-01-07 00:00:00 | 1 | 4 | 2 |
| 2017-01-05 00:00:00 | 1 | 5 | 2 |
| 2017-01-03 00:00:00 | 1 | 6 | 2 |
| 2017-01-01 00:00:00 | 1 | 7 | 2 |
| 2017-01-13 00:00:00 | 1 | 1 | 3 |
| 2017-01-01 00:00:00 | 1 | 2 | 3 |
| 2017-01-03 00:00:00 | 1 | 1 | 4 |
| 2017-01-01 00:00:00 | 1 | 2 | 4 |
| 2017-01-02 00:00:00 | 1 | 1 | 5 |
+---------------------+--------+------+---------+
We now simply wrap this in another statement to select the avg where the row number is less than or equal to seven.
select e1.row day, avg(e1.rating) avg
from (
select e.created_at,
e.rating,
if(#lastUser=user_id,#row := #row+1, #row:=1) as row,
#lastUser:= e.user_id as user_id
from entries e,
( select #row := 0, #lastUser := 0 ) vars
order by e.user_id asc,
e.created_at desc) e1
where e1.row <=7
group by e1.row;
This outputs:
+------+--------+
| day | avg |
+------+--------+
| 1 | 1.0000 |
| 2 | 1.0000 |
| 3 | 1.0000 |
| 4 | 1.0000 |
| 5 | 1.0000 |
| 6 | 1.0000 |
| 7 | 1.0000 |
+------+--------+

how to insert average of a query into mysql table

I am trying to get average of latency for each items that holds into two separate mysql table. Let me more clarify that I have two mysql tables as below,
table: monitor_servers
+-----------+-----------------+
| server_id | label |
+-----------+-----------------+
| 1 | a.com |
| 2 | b.com |
+-----------+-----------------+
table: monitor_servers_uptime
+-------------------+-----------+-----------+
| servers_uptime_id | server_id | latency |
+-------------------+-----------+-----------+
| 1 | 1 | 0.4132809 |
| 3 | 1 | 0.4157769 |
| 6 | 1 | 0.4194210 |
| 9 | 1 | 0.4140880 |
| 12 | 2 | 0.4779439 |
| 15 | 2 | 0.4751789 |
| 18 | 2 | 0.4762829 |
| 22 | 2 | 0.4706681 |
+-------------------+-----------+-----------+
Basically, each domains associated with the same id_number in both tables. While I am running the query below, getting average of each items.
select monitor_servers.label, avg(monitor_servers_uptime.latency)
from monitor_servers,monitor_servers_uptime
where monitor_servers.server_id = monitor_servers_uptime.server_id
group by monitor_servers.server_id;
The query ended up,
+---------------------+-------------------------------------+
| label | avg(monitor_servers_uptime.latency) |
+---------------------+-------------------------------------+
| a.com | 0.41393792995 |
| b.com | 0.47551423171 |
+---------------------+-------------------------------------+
My questions are doing am i in wright way while getting average of the each items and how can i insert new average result of each items into a new column on table monitor_servers ? And also what happens if some of latency rows are NULL ?
**Edit : What i am trying to achieve in one query result is **
+-----------+----------+------------------+
| server_id | label | avg. |
+-----------+----------+------------------+
| 1 | a.com | 0.41393792995 |
| 2 | b.com | 0.47551423171 |
+-----------+-----------------------------+
Thanks in advance,
Your calculation seems to be correct.
You could add another column to the monitor_servers using sql:
ALTER TABLE monitor_servers ADD avg_latency DEFAULT 0.0 NOT NULL
For doing the AVG calculation check this answer.

Listing results with unique column

I want to list top 6 race records with unique holder only. I mean a holder gets in the list shouldn't be listed with his another record. I currently use the query below to list top 6 times.
mysql> select * from racerecords order by record_time asc, date asc;
+----+---------+------------+-------------+---------------------+----------+
| id | race_id | holder | record_time | date | position |
+----+---------+------------+-------------+---------------------+----------+
| 2 | 10 | Stav | 15 | 2014-08-11 19:43:49 | 1 |
| 1 | 10 | Jennifer | 15 | 2014-08-13 19:43:19 | 1 |
| 4 | 10 | Jennifer | 16 | 2014-08-02 19:44:27 | 1 |
| 5 | 10 | Osman | 17 | 2014-08-04 19:44:57 | 1 |
| 7 | 10 | Gokhan | 18 | 2014-08-15 19:45:37 | 1 |
| 3 | 10 | MotherLode | 25 | 2014-08-01 19:44:11 | 1 |
+----+---------+------------+-------------+---------------------+----------+
6 rows in set (0.00 sec)
As you can see the holder "Jennifer" is listed twice. I want mySQL to skip her after she got in the list. The result I want to be generated is:
+----+---------+------------+-------------+---------------------+----------+
| id | race_id | holder | record_time | date | position |
+----+---------+------------+-------------+---------------------+----------+
| 2 | 10 | Stav | 15 | 2014-08-11 19:43:49 | 1 |
| 1 | 10 | Jennifer | 15 | 2014-08-13 19:43:19 | 1 |
| 5 | 10 | Osman | 17 | 2014-08-04 19:44:57 | 1 |
| 7 | 10 | Gokhan | 18 | 2014-08-15 19:45:37 | 1 |
| 3 | 10 | MotherLode | 25 | 2014-08-01 19:44:11 | 1 |
+----+---------+------------+-------------+---------------------+----------+
I tried everything. GROUP BY holder generates wrong results. It gets the very first record of the holder, even though is not the best. In this table it generates an output like above because id:1 is the first record I inserted for Jennifer.
How can I generate output a result like above?
Desired result can be achieved through this query but it performance intensive. I have reproduced the result in SQLFilddle http://sqlfiddle.com/#!2/f8ee7/3
select * from racerecords
where
(HOLDER, RECORD_TIME) in (
select HOLDER,min(RECORD_TIME) from racerecords
group by HOLDER)
Seems you have missed to include the Where clause in the sub-query. Try this
select * from racerecords
where
(HOLDER, RECORD_TIME) in (
select HOLDER,min(RECORD_TIME) from racerecords where race_id =17
group by HOLDER )
And race_id =17
Order by RECORD_TIME
you should use distinct clause
SELECT DISTINCT column_name,column_name
FROM table_name;
looks this http://www.w3schools.com/sql/sql_distinct.asp