I got a table that is being updated every 5 Minutes by a Script Call, triggered by a http request. The table looks like this
MariaDB > select host_id, ping_eu, disk_usage,insert_time from sequencial_host;
+---------+---------+-------------+---------------------+
| host_id | ping | disk_usage | insert_time |
+---------+---------+-------------+---------------------+
| 1 | 35.60 | 10329416704 | 2016-01-20 20:47:51 |
| 2 | 36.57 | 2902848512 | 2016-01-20 20:48:06 |
+---------+---------+-------------+---------------------+
I want to prevent a mess in case the script runs amok or any malicious intent by calling a stored procedure that allows inserting if the last insert_time was at least 4,5 Minutes ago. (30 second script built in time out, so more a less accurate).
I built this Procedure:
CREATE PROCEDURE `update_table`(
IN host_id_a int(11),
IN insert_time timestamp,
IN disk_usage bigint(20) ,
IN ping decimal(10,2)
BEGIN
DECLARE count INT;
SELECT count(insert_time) INTO count FROM db.sequencial_host WHERE insert_time > (NOW() - INTERVAL 270 SECOND) AND host_id = host_id_a ORDER BY insert_time DESC LIMIT 1;
IF count < 1 THEN
INSERT INTO db.sequencial_host ( `host_id`,`insert_time`,`disk_usage`,`ping`)
VALUES( host_id_a, CURRENT_TIMESTAMP, disk_usage, ping);
END IF;
END
If I leave away the If clause, the Insert Statements work by calling the procedure and the Select Query also works for itself if executed in the shell.
I am using MariaDB 5.5.46-1ubuntu0.14.04.2.
I nearly tried any combination of IF count = 0, IF count IS NULL, IF (SELECT ..) IS NULL.
Could anybody take look?
With kind regards Joey
EDIT:
MariaDB [tempdb]> SELECT count(*) FROM tempdb.sequencial_host WHERE insert_time > (NOW() - INTERVAL 270 SECOND) AND host_id = 1 ORDER BY insert_time DESC LIMIT 1;
+--------------------+
| count(insert_time) |
+--------------------+
| 0 |
+--------------------+
1 row in set (0.01 sec)
MariaDB [tempdb]> SELECT count(*) FROM tempdb.sequencial_host WHERE insert_time > (NOW() - INTERVAL 2 HOUR) AND host_id = 1 ORDER BY insert_time DESC LIMIT 1;
+--------------------+
| count(insert_time) |
+--------------------+
| 1 |
+--------------------+
Edit 2:
Pastebin containing all relevant database information in complete. pastebin.com/99eEDLy4
Edit 3:
If I change it to cnt = 1 I can spam the query and it fills everything, but only if there is data in the table. With a freshly truncated one I need to set it to 0 to get values in there.
count cant never < 0 . count is everytime a positive number between 0 and n. So
IF count < 0 THEN
never works
change it to
IF count > 0 THEN
Count on a column that is or has null, will only return count of those values which are not null. If all column values are null then null is returned.
In this case your where clause is ensuring no records return thus all column values for insert_Time will be null; thus null is returned. However, if you had multiple records without null insert_time; then the count would represent those records without a null insert_time.
I think you want count(*) instead of count(insert_time) to ensure 0 is returned when no records are found.
CREATE PROCEDURE `update_table`(
IN host_id_a int(11),
IN insert_time timestamp,
IN disk_usage bigint(20) ,
IN ping decimal(10,2))
BEGIN
DECLARE count INT;
SELECT count(*) INTO count FROM db.sequencial_host WHERE insert_time > (NOW() - INTERVAL 270 SECOND) AND host_id = host_id_a ORDER BY insert_time DESC LIMIT 1;
IF count < 1 THEN
INSERT INTO db.sequencial_host ( `host_id`,`insert_time`,`disk_usage`,`ping`)
VALUES( host_id_a, CURRENT_TIMESTAMP, disk_usage, ping);
END IF;
END
It's not a bug, it's a feature.
Inside of a Stored Procedure Select Exists returns 1 when there is data in the table and null if there is no, when the query is written like that (without specific table set in the where clause):
SELECT EXISTS (SELECT * INTO count FROM db.sequencial_host WHERE insert_time > (NOW() - INTERVAL 270 SECOND) AND host_id = host_id_a ORDER BY insert_time DESC LIMIT 1);
If you append the table to the host id it works as expected. Even counts return the expected value. SELECT EXISTS (SELECT * INTO count FROM db.sequencial_host WHERE insert_time > (NOW() - INTERVAL 270 SECOND) AND sequencial_host.host_id = host_id_a ORDER BY insert_time DESC LIMIT 1);
Related
I have a table of questions in mysql with 5 different types of questions like follow :
table decouverte :
id | question | answer | type | date
----------------------------------------------------
1 | txt question1 | theAnswer1 | easy | null
2 | txt question2 | theAnswer2 | normal | null
3 | txt question3 | theAnswer3 | difficult | null
4 | txt question4 | theAnswer4 | hard | null
5 | txt question5 | theAnswer5 | easy | null
.
.
I would like to get everyday at midnight one random question of each type where the date is either null or current date.
For the moment I have this :
$stmt = $this->db->prepare("CREATE EVENT get_5_dailyQuestions
ON SCHEDULE EVERY 1 DAY STARTS (TIMESTAMP(CURRENT_DATE) + INTERVAL 1 DAY + INTERVAL 1 HOUR)
DO SELECT * from decouverte where type = 'easy' AND (date is NULL OR date = CURDATE()) ORDER BY RAND() LIMIT 1 UNION
SELECT * from decouverte where type = 'normal' AND (date is NULL OR date = CURDATE()) ORDER BY RAND() LIMIT 1 UNION
SELECT * from decouverte where type = 'difficult' AND (date is NULL OR date = CURDATE()) ORDER BY RAND() LIMIT 1 UNION
SELECT * from decouverte where type = 'hard' AND (date is NULL OR date = CURDATE()) ORDER BY RAND() LIMIT 1");
The create event works, but not the SELECT thing.
Can anyone help me ? Thank you in advance
Personally, I would just create a result table to store the randomly generated data from the base table . Then we can query the result table. This is of course done in an event on a daily basis. Here is code written and tested in workbench. (I set the interval to 1 minute ) By the way, I made some adjustment to your query. Instead of 4 separate SELECT statements, we can accomplish the job in one.
set ##global.event_scheduler=on;
create table chosen_result like decouverte;
delimiter //
CREATE EVENT get_5_dailyQuestions
ON SCHEDULE EVERY 1 minute STARTS now() DO
BEGIN
truncate chosen_result;
insert chosen_result
select * from decouverte t1
join
(select (select id from decouverte where type=d.type order by rand() limit 1) as id
from decouverte d
where date is null or date=sysdate()
group by type
) t2
using(id);
END//
delimiter ;
I'm trying to get the hours worked per day by id, the table has two columns (id, registro):
id registro
1 2017-08-23 08:00:00 -starting working day
1 2017-08-23 12:00:00 -stop for lunching time
2 2017-08-23 12:40:00
1 2017-08-23 13:00:00 - return from lunching time
1 2017-08-23 17:00:00 - going home
Then I copied the idea from : Get total hours worked in a day mysql in order to create a column in_out and got this:
set #num := 0;
select tab.id, date_format(`registro`,'%d/%m/%Y') as date_,
SUM(UNIX_TIMESTAMP(`registro`)*(1-2*`in_out`))/3600 AS `hours_worked`
from (select id, registro, mod(#num := #num+1,2) as in_out from registro_ponto_v
where id= 1
and date_format(registro,"%d-%m-%Y") = '23-08-2017'
order by registro asc) as tab
group by tab.id, date_;
When I execute on (mysql 14.14 dist 5.7.16 (x86_64) it result 8hs correctly, but when I execute on server version 10.1 on debian 9 for some reason the in_out column doesn't show the same order of rows in subselect... (8hs - 1; 12hs - 0; 13hs -1; 17hs -0)
and so the sum of hours get an value different. Then my point is: there's a way to garantee that the order of rows match to this:
id registro in_out
1 8hs 1
1 12hs 0
1 13hs 1
1 17hs 0
I think this would be simpler if you were able to explicitly store 'ins' and 'outs'...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id INT NOT NULL
, registro DATETIME NOT NULL
,status ENUM('in','out') NOT NULL
,PRIMARY KEY(id,registro)
);
INSERT INTO my_table VALUES
(1,'2017-08-23 08:00:00','in'),
(1,'2017-08-23 12:00:00','out'),
(2,'2017-08-23 12:40:00','in'),
(1,'2017-08-23 13:00:00','in'),
(1,'2017-08-23 17:00:00','out');
SELECT id
, DATE(user_in) date
, SEC_TO_TIME(SUM(TIME_TO_SEC(TIMEDIFF(user_out,user_in)))) total
FROM
( SELECT x.id,x.registro user_in,MIN(y.registro) user_out
FROM my_table x
JOIN my_table y
ON y.id = x.id
AND y.registro > x.registro
AND y.status = 'out'
WHERE x.status = 'in'
GROUP
BY x.id, x.registro
) a
GROUP
BY id,date;
+----+------------+----------+
| id | date | total |
+----+------------+----------+
| 1 | 2017-08-23 | 08:00:00 |
+----+------------+----------+
I have this table:
// cookies
+---------+-------------------------+------------------+------------+
| id | email | cookie | date_time |
+---------+-------------------------+------------------+------------+
| int(11) | varchar(50) | varchar(128) | int(11) |
+---------+-------------------------+------------------+------------+
| 1 | jack_2009#gmail.com | ojer0f934mf2... | 1467204523 |
| 2 | peter.zm#yahoo.com | ko4398f43043... | 1467205521 |
| 3 | matrix_john23#gmail.com | 34fjkg3j438t... | 1467205601 |
| 4 | peter.zm#yahoo.com | 0243hfd348i4... | 1467206039 |
+---------+-------------------------+------------------+------------+
And here is my query:
INSERT INTO cookies VALUES(NULL, $email, $hash, unix_timestamp())
Now I need to check following condition before inserting:
The number of rows (for specific user) should be less than:
5 per hour
10 per day
50 per month
100 per total
I just can check the last case:
INSERT INTO cookies(id, email, cookie, date_time)
SELECT NULL, $email, $hash, unix_timestamp()
FROM cookie
WHERE email = $email AND
100 >= ( SELECT count(1) FROM cookies WHERE email = $email )
Well, how can I add other conditions?
I'm not positive on whether the >'s (in the group by) should be >='s, but I think this will do what you are asking.
INSERT INTO cookies(id, email, cookie, date_time)
SELECT NULL, $email, $hash, unix_timestamp()
FROM cookie
WHERE email = $email
AND NOT EXISTS (
SELECT COUNT(CASE WHEN date_time > UNIX_TIMESTAMP(now() - INTERVAL 1 HOUR)
THEN 1 ELSE NULL END) AS rowsInLastHour
, COUNT(CASE WHEN date_time > UNIX_TIMESTAMP(now() - INTERVAL 1 DAY)
THEN 1 ELSE NULL END) AS rowsInLastDay
, COUNT(CASE WHEN date_time > UNIX_TIMESTAMP(now() - INTERVAL 1 MONTH)
THEN 1 ELSE NULL END) AS rowsInLastMonth
, COUNT(1) AS rowsEver
FROM cookie
WHERE email = $email
HAVING rowsInLastHour > 5
OR rowsInLastDay > 10
OR rowsInLastMonth > 50
OR rowsEver > 100
)
;
It counts all the rows (for the email) that had date_time values in the last hour|day|month by using now() - INTERVAL 1 HOUR|DAY|MONTH to find when the last hour|day|month started, and counting those values that occurred after those starting times.
It then uses the HAVING to only yield the singular result (aggregation such as COUNT that does not have an associated GROUP BY clause always results in 1 row), if any of the limits you specified were exceeded.
Then the NOT EXISTS returns true if there were no results (because the limits were not exceeded).
Edit: Updated comparisons to use unit timestamps, as needed by question.
You can use stored procedure and within that you can handle this. Pass your insert values to this stored procedure
DELIMITER $$
CREATE PROCEDURE `sp_test`(id int, email varchar(45), cookie varchar(45), date_time datetime)
BEGIN
DECLARE countval INT;
SET countval = (SELECT sum(1) FROM cookies WHERE email = $email );
IF (countval is null) THEN
// do something
ELSEIF (countval>10) THEN
// do something like that
ELSE
// do something
END IF;
// insert query
END $$
DELIMITER ;
I've got a table with some data. It looks like that:
MariaDB [lokatnik]> SELECT id_elixir, start_time FROM elixir ORDER BY start_time ASC;
+-----------+------------+
| id_elixir | start_time |
+-----------+------------+
| 3 | 00:00:00 |
| 1 | 12:30:00 |
| 2 | 13:30:00 |
+-----------+------------+
3 rows in set (0.00 sec)
Now, I need to select exactly one row matching time condition - say - start_time > some_time.
For example:
when some_time is 10:00, then I need a row with id_elixir = 1 (first row with time more than 10:00),
when some_time is 13:00, then I need a row with id_elixir = 2 (like above),
when some_time is 14:00, then I need row with id_elixir = 3.
How can I get this row using SQL (and MySQL/MariaDB DBMS)?
Maybe, something like this will work for you:
SELECT
*
FROM
elixir
WHERE
CASE
WHEN start_time BETWEEN CAST('10:00:00' AS TIME) AND CAST('13:00:00' AS
TIME)
THEN id =1
WHEN start_time BETWEEN CAST('13:00:00' AS TIME) AND CAST('14:00:00' AS
TIME)
THEN id =2
WHEN start_time > CAST('14:00:00' AS TIME)
THEN id =3
ELSE true
But you can actually check that by using simple boolean operators:
SELECT
*
FROM
elixir
WHERE
(
start_time BETWEEN CAST('10:00:00' AS TIME) AND CAST('13:00:00' AS TIME)
AND id =1
)
OR
(
start_time BETWEEN CAST('13:00:00' AS TIME) AND CAST('14:00:00' AS TIME)
AND id =2
)
OR
(
start_time > CAST('14:00:00' AS TIME)
AND id =3
I have a query which takes the last update date (timestamp but as a bigint(20) column) like this:
SELECT a.id_workorder, MAX(b.update_date) AS udpate_date
FROM main_log a,
(
SELECT MAX(log_date) AS update_date, log_id
FROM log_a
GROUP BY log_id
UNION
SELECT MAX(log_date) AS update_date, log_id
FROM log_b
GROUP BY log_id
)b
WHERE a.id_log = b.log_id
GROUP BY b.log_id
and it returns the last update date (unix timestamp as a bigint(20)) for any kind of log (a or b):
id last update
-------------------------
1001 1376750476349
1002 1376753690861
1003 1378122801986
1004 1377764414858
1005 1377847226096
...
Now I want to format the return in date format and I naively though I can just format the outside timestamp with FROM_UNIXTIME like this:
SELECT
a.id_workorder,
FROM_UNIXTIME(MAX(b.update_date)) AS udpate_date
FROM main_log a,
(
SELECT MAX(log_date) AS update_date, log_id
FROM log_a
GROUP BY log_id
UNION
SELECT MAX(log_date) AS update_date, log_id
FROM log_b
GROUP BY log_id
)b
WHERE a.id_log = b.log_id
GROUP BY b.log_id
but it gives
id last update
-------------------------
1001 null
1002 null
1003 null
1004 null
1005 null
...
I tried to put the conversion in the inner queries as well but it is the same.
I also tried to find answers on SO, mySQL documentation and Google but did not find why the conversion does not works when I make a group by.
Your timestamp is in milliseconds try:
SELECT a.id_workorder,
FROM_UNIXTIME(MAX(b.update_date/1000)) AS udpate_date
FROM main_log a, ...
(i.e. divide the time by 1000 to get seconds)
mysql> select FROM_UNIXTIME(1376750476349);
+------------------------------+
| FROM_UNIXTIME(1376750476349) |
+------------------------------+
| NULL |
+------------------------------+
1 row in set (0.06 sec)
mysql> select FROM_UNIXTIME(1376750476349/1000);
+-----------------------------------+
| FROM_UNIXTIME(1376750476349/1000) |
+-----------------------------------+
| 2013-08-17 15:41:16 |
+-----------------------------------+
1 row in set (0.02 sec)
mysql>
SET #DateFormat ='%m/%d/%Y';
SET #DateTimeFormat ='%m/%d/%Y %h:%i %p';
SELECT DATE_FORMAT(timestamp(NOW()+0), #DateFormat);
SELECT DATE_FORMAT(timestamp(NOW()+0), #DateTimeFormat);
You can use the above code to change the bigint timestamp to the Actual date or date-time.