How to use Event Scheduler based on two tables? - mysql

CREATE EVENT sensor_checker
ON SCHEDULE EVERY 15 MINUTE
DO
UPDATE tb_device d
inner join arduino_id a
on d.id_device = a.id_node
SET d.status_device = 0 WHERE ADDTIME(new.date_server, '900') < now()
So I have two tables tb_arduino and tb_device, I am trying to create an event that when column 'date_server' (from tb_arduino table) is less then 15 minute from current time the column 'status_device'(from tb_device) will update the value of the column 'status_device' to 0 else the column update the value to 1. But the query above doesn't seems to work.
More explanation:
If the current time is 12:20:40
'tb_arduino' table
| id_node | luminance | date_server |
| -------- | -------- | -------- |
| s01 | 270 | 2023-27-01 12:20:30 |
| s01 | 280 | 2023-27-01 12:10:30 |
| s02 | 210 | 2023-27-01 12:00:30 |
| s02 | 230 | 2023-27-01 11:55:30 |
Then the value 'status_device' of 'id_node' s02 is become 0
'tb_device' table
| id_device | status_device |
| -------- | -------- |
| s01 | 1 |
| s02 | 0 |

Test this:
CREATE EVENT sensor_checker
ON SCHEDULE EVERY 15 MINUTE
DO
UPDATE tb_device
SET status_device = EXISTS (
SELECT NULL
FROM arduino_id
WHERE tb_device.id_device = arduino_id.id_node
AND arduino_id.date_server >= NOW() - INTERVAL 15 MINUTE
);
Of course firstly test UPDATE query separately.

Related

MySQL Generate random timestamps from range, order it by date asc, then use it to update missing values from another table

I have a table "ips", where I store my download logs. Accidentally, I forgot to add timestamp for it (yea, stupid mistake)... Now, I have fixed it, but there are already 65.5k entries without timestamp.. Is there a way, how to add random timestamp from date range to fill NULL timestamps?
I was able to generate timestamps list using this queries:
SET #MIN = '2020-04-05 18:30:00';
SET #MAX = NOW();
SELECT TIMESTAMPADD(SECOND, FLOOR(RAND() * TIMESTAMPDIFF(SECOND, #MIN, #MAX)), #MIN) as dldate FROM ips WHERE name="filename1" ORDER BY dldate ASC;
It generated the exact count of entries I need for specific filename, but I have absolutely no idea, how to use this list to update already existing entries in my "ips" table and KEEP IT ORDERED by "dldate"...
When I was testing it, I was close, when I used this query (I was afraid to use UPDATE to not mess my data up, so I used just SELECT):
SELECT ips.id, ips.name, t1.dldate FROM (SELECT id, name FROM ips WHERE name="filename1") ips INNER JOIN (SELECT ips.id as id, TIMESTAMPADD(SECOND, FLOOR(RAND() * TIMESTAMPDIFF(SECOND, #MIN, #MAX)), #MIN) as dldate FROM ips WHERE name="filename1" ORDER BY dldate ASC) t1 ON (ips.id=t1.id) ORDER BY ips.id ASC;
That worked, but timestaps are purely random (obviously :D), and I need them to "respect" id from "ips" table (starting with lower timestamp for lowest id, and then continuously higher timestamps for higher ids).
I'm getting this:
+------+-----------+---------------------+
| id | name | dldate |
+------+-----------+---------------------+
| 15 | filename1 | 2020-12-18 21:35:03 |
| 1118 | filename1 | 2020-12-18 13:34:47 |
| 1141 | filename1 | 2020-08-07 12:49:46 |
| 1142 | filename1 | 2020-11-29 00:43:31 |
| 1143 | filename1 | 2020-05-13 03:00:16 |
| 1286 | filename1 | 2020-12-14 09:58:50 |
| 1393 | filename1 | 2021-04-14 06:45:23 |
| 1394 | filename1 | 2021-03-03 17:42:25 |
| 1395 | filename1 | 2020-09-03 05:56:56 |
| .... |
|62801 | filename1 | 2021-01-05 21:21:29 |
+------+-----------+---------------------+
And I would like to get this:
+------+-----------+---------------------+
| id | name | dldate |
+------+-----------+---------------------+
| 15 | filename1 | 2020-04-05 21:35:03 |
| 1118 | filename1 | 2020-04-18 13:34:47 |
| 1141 | filename1 | 2020-05-07 12:49:46 |
| 1142 | filename1 | 2020-06-29 00:43:31 |
| 1143 | filename1 | 2020-08-13 03:00:16 |
| 1286 | filename1 | 2020-10-14 09:58:50 |
| 1393 | filename1 | 2020-12-14 06:45:23 |
| 1394 | filename1 | 2021-01-03 17:42:25 |
| 1395 | filename1 | 2021-03-03 05:56:56 |
| .... |
|62801 | filename1 | 2021-04-29 14:21:29 |
+------+-----------+---------------------+
Is there any way, how to achieve this output and how to use it with UPDATE statement instead of SELECT with INNER JOIN?
Thank you for help!
How about just starting with a date and adding a time unit?
update ips
set timestamp = '2000-01-01' + interval id second
where timestamp is null;
I'm not sure if second is the right unit. Or if '2000-01-01' is a good base time. But this gives you an approach for doing what you want.
You can, of course, test this using a select first.
If you do want randomness, you can do something like this:
select ips.*,
'2021-04-01' - interval running second
from (select ips.*,
sum(rnd) over (order by id desc) as running
from (select ips.*,
rand() * 1000 as rnd
from ips
where timestamp is null
) ips
) ips;
This calculates a random number of seconds. Then it does a revenue cumulative sum . . . and subtracts those seconds from a base date.

MySQL query for data by step size in a given range

So basically I have a users table which has a column named "completed_surveys" which holds total number of completed surveys.
I need to create a query which would take step size number and would group them by that range.
Example result which would suit my needs:
+---------+-------------------+
| range | completed_surveys |
+---------+-------------------+
| 0-14 | 4566 |
| 14-28 | 3412 |
| 28-42 | 5456 |
| 42-56 | 33 |
| 56-70 | 31 |
| 70-84 | 441 |
| 84-98 | 576 |
| 98-112 | 23 |
| 112-126 | 12 |
| 126-140 | 1 |
+---------+-------------------+
What I have so far:
select concat(what should i add here??) as `range`,
count(users.completed_surveys) as `completed_surveys` from users WHERE users.completed_surveys > 0 group by 1 order by users.completed_surveys;
I think this query is correct however in the concat function I don't really know how to increase the previous number by 14. Any ideas?
One idea is to first create a helper table with values 0..9 .
CREATE TABLE tmp ( i int );
INSERT INTO tmp VALUES (0) , (1) ... (9);
Then join the two tables:
SELECT concat(i,' - ',(i+1)*7) as `range`,
count(users.completed_surveys) as `completed_surveys` from users
INNER JOIN tmp ON (users.completed_surveys>tmp.i AND users.completed_surveys<=(i+1)*7)
WHERE users.completed_surveys > 0
GROUP BY tmp.i
ORDER BY tmp.i

Using computed value or trigger to update a row with data from a different row in the same table?

I wish to do an insert for 2019-08-15 for mainKey = 1 and if the total is bigger than 200 on 2019-08-14 I want to save the difference in last column in the new row that was created for 2019-08-15. The total is a value that can stay the same or increase.
+------------+---------+-------+--------------------------+
| date | mainKey | total | differenceSinceYesterday |
+------------+---------+-------+--------------------------+
| 2019-08-14 | 1 | 200 | 0 |
| 2019-08-14 | 2 | 500 | 0 |
+------------+---------+-------+--------------------------+
date and mainKey together uniquely identify the row so a row for specific mainKey can only be inserted once a day.
+--------------------------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------------------------+---------+------+-----+---------+-------+
| date | date | NO | PRI | NULL | |
| mainKey | int(11) | NO | PRI | NULL | |
| total | int(11) | NO | | NULL | |
| differenceSinceYesterday | int(11) | NO | | NULL | |
+--------------------------+---------+------+-----+---------+-------+
I looked into triggers but if ran AFTER UPDATE it becomes recursive because it is the same table. Or should I transfer the logic to an external script that does inserts in the first place and run another SELECT to find 1 day old row with specific mainKey and update the today's row?
You can achieve what you want with a BEFORE INSERT trigger, checking for a total value from the previous day of >= 200, and if it exists, setting the differenceSinceYesterday value to the difference between today's and yesterday's totals:
DELIMITER //
CREATE TRIGGER updateDifference
BEFORE INSERT ON table1
FOR EACH ROW
BEGIN
DECLARE yesterday INT;
SELECT total INTO yesterday FROM table1 WHERE date = NEW.date - INTERVAL 1 DAY AND mainKey = NEW.mainKey;
IF yesterday >= 200 THEN
SET NEW.differenceSinceYesterday = NEW.total - yesterday;
END IF;
END;
DELIMITER ;
INSERT INTO table1 (date, mainKey, total)
VALUES ('2019-08-15', 1, 400), ('2019-08-16', 1, 300);
SELECT * FROM table1
Output:
date mainKey total differenceSinceYesterday
2019-08-14 00:00:00 1 200 0
2019-08-14 00:00:00 2 500 0
2019-08-15 00:00:00 1 400 200
2019-08-16 00:00:00 1 300 -100
Demo on dbfiddle

Adding one hour to datetime and comparing with present time based on hours and minutes

I have written a query where I am adding one hour to a datetime which is present in database, and comparing that time to the present time based on minutes and hours.
Table structure of upgrades:
id| postid |type |status| counter| datetime | autoreposttime
1 | 139 | M | P | 1 | 2017-04-26 10:49:23 | 60
2 | 140 | M | P | 1 | 2017-04-26 10:49:27 | 60
3 | 141 | M | P | 1 | 2017-04-26 10:49:31 | 60
4 | 142 | M | P | 1 | 2017-04-26 10:49:34 | 60
Table structure of posts:
post_id | locationid | priority_time
81 | 1 | 2017-04-20 18:29:17
82 | 27 | 2017-04-20 18:29:19
85 | 27 | 2017-04-20 18:29:07
Here is my SQL query which I have written in where I want to retrieve rows where datetime + 1 hour is equal to present time.
$posts = DB::table('upgrades')
->join('posts','posts.post_id','=','upgrades.postid')
->where(DB::raw('DATE_FORMAT(DATE_ADD(`upgrades`.`datetime`, INTERVAL 1 HOUR),"%Y-%m-%d %H:%i")'),$datetime)
->select('posts.*','upgrades.*')->get();
I'm getting data as null, and I think there's some problem in the where condition. What is wrong with my query?
One way to do that is to use Carbon. If I understood you correctly, you want result for specified minute, so here's an example:
$time = Carbon::now()->subHour();
$posts = ....
->whereBetween('datetime', [$time->format('Y-m-d H:i:00'), $time->format('Y-m-d H:i:59'])
....
->get();

How can I select all rows which have been inserted in the last day?

I have a table like this:
// reset_password_emails
+----+----------+--------------------+-------------+
| id | id_user | token | unix_time |
+----+----------+--------------------+-------------+
| 1 | 2353 | 0c274nhdc62b9dc... | 1339412843 |
| 2 | 2353 | 0934jkf34098joi... | 1339412864 |
| 3 | 5462 | 3408ujf34o9gfvr... | 1339412894 |
| 4 | 3422 | 2309jrgv0435gff... | 1339412899 |
| 5 | 3422 | 34oihfc3lpot4gv... | 1339412906 |
| 6 | 2353 | 3498hfjp34gv4r3... | 1339412906 |
| 16 | 2353 | asdf3rf3409kv39... | 1466272801 |
| 7 | 7785 | 123dcoj34f43kie... | 1339412951 |
| 9 | 5462 | 3fcewloui493e4r... | 1339413621 |
| 13 | 8007 | 56gvb45cf3454g3... | 1339424860 |
| 14 | 7785 | vg4er5y2f4f45v4... | 1339424822 |
+----+----------+--------------------+-------------+
Each row is an email. Now I'm trying to implement a limitation for sending-reset-password email. I mean an user can achieve 3 emails per day (not more).
So I need an query to check user's history for the number of emails:
SELECT count(1) FROM reset_password_emails WHERE token = :token AND {from not until last day}
How can I implement this:
. . . {from now until last day}
Actually I can do that like: NOW() <= (unix_time + 86400) .. But I guess there is a better approach by using interval. Can anybody tell me what's that?
Your expression will work, but has 3 problems:
the way you've coded it means the subtraction must be performed for every row (performance hit)
because you're not using the raw column value, you couldn't use an index on the time column (if one existed)
it isn't clear to read
Try this:
unix_time > unix_timestamp(subdate(now(), interval '1' day))
here the threshold datetime is calculated once per query, so all of the problems above have been addressed.
See SQLFiddle demo
You can convert your unix_time using from_unixtime function
select r.*
from reset_password_emails r
where now() <= from_unixtime(r.unix_time) - interval '1' day
Just add the extra filters you want.
See it here: http://sqlfiddle.com/#!9/4a7a9/3
It evaluates to no rows because your given data for unix_time field is all from 2011
Edited with a sqlfiddle that show the conversion:
http://sqlfiddle.com/#!9/4a7a9/4