how to trigger MySQL query when DATETIME is past - mysql

Good evening,
I have 1 table where i store a element with the type : datetime,
I am going to insert a date into this element by a script on php.
When the date is reached i want to increase "datepast" from table2,
we can do it by comparing "name" from table1 with "person_name" from table2.
Now the question is how to trigger a sql script to do this job for me, it would be great if it was real time.
Already thanks,
create table if not exists table1 (
name varchar(64) NOT NULL,
finishtime DATETIME,
id_table1 int NOT NULL AUTO_INCREMENT,
primary key ( id_table1 ));
create table if not exists table2 (
person_name varchar(64) NOT NULL,
datepast int,
id_table1 int NOT NULL AUTO_INCREMENT,
primary key ( id_table1 ));

Cronjobs and MySQL events can do a good job handling such things. But the queries you put in there must be set up to be idempotent -- that is, they must be set up so if you run them more than once they have the same effect as running them once. Otherwise you will have a brittle solution.
When you're handling data based on expiration times like your finishtime, it's usually a good idea to try to use a query or a view, rather than an update.
For example you could create this view
CREATE OR REPLACE VIEW table2 AS
SELECT name AS person_name
COUNT(*) AS datepast
FROM table1
WHERE datepast <= NOW()
GROUP BY name
Then, SELECT * FROM table2 will generate a result set just like SELECT person_name, datepast FROM table2 might generate. But the SELECT resultset will always be precisely accurate in time.
Wait! you say, isn't that inefficient? The answer is, probably not unless you have several hundred thousand rows or more in your table. SQL is built for this kind of declarative data stuff.

You can use MySQL events or a Cronjob. It is not real-time, but it can be close to.
Because your posting is not complete and table2 is missing, I can only give you an example how to setup an event. Inside DO BEGIN and END $$ you can add your MySQL query to update datepast.
DELIMITER $$
CREATE
EVENT `increase_date_past`
ON SCHEDULE EVERY 30 SECOND
DO BEGIN
{{READ DATE AND UPDATE TABLE2}}
END $$
DELIMITER ;
Events are not activated by default, you have to go to
In /etc/mysql/my.cnf
and activate events by placing a line inside [mysqld] tag.
[mysqld]
event_scheduler=1
and then sudo service mysql restart

Related

MYSQL event on every 1 minute not working

First, this is OpenCart
I have two tables:
1. oc_product (product_id, model, price, event_start, event_end and etc.)
2. oc_product_to_category (product_id, category_id)
Every product has Start Date and End Date. I created MYSQL event that catch every product with expired date (event_end < NOW()) to store it in category "Archive" with id = 68
Here is the code of MYSQL EVENT
CREATE EVENT move_to_archive_category
ON SCHEDULE EVERY 1 MINUTE
STARTS NOW()
DO
INSERT INTO `oc_product_to_category` (product_id, category_id)
SELECT product_id, 68 as category_id
FROM oc_product p WHERE p.event_end < NOW() AND p.event_end <> '0000-00-00';
When event starts it works properly! BUT, when I got to administration and publish new product with expired date I'm waiting 1 minute to see the product in "Archive" category but nothing happens.
I saw in "SHOW PROCESSLIST" and everything is OK:
event_scheduler localhost NULL Daemon 67 Waiting for next activation NULL
and also "SHOW EVENTS" looks good
Db Name Definer Time zone Type Execute at Interval value Interval field Starts Ends Status Originator character_set_client collation_connection Database Collation
events move_to_archive_category root#localhost SYSTEM RECURRING NULL 1 MINUTE 2016-08-15 13:37:54 NULL ENABLED 1 utf8 utf8_general_ci utf8_general_ci
I'm working locally, not live
Any ideas?
Thanks in advance! :)
I suggest turning on the sonar. I have 3 event links hanging off my profile page. So I created a few helper tables (that can also be seen in those links) to assist is turning on the sonar to see what is up in your events. Note you can expand on it for performance tracking as I did in those links.
Remember that Events succeed or fail (in your mind) based on the data and they do so silently. But tracking what is going on, you can vastly increase you happiness level when developing in them.
Event:
DROP EVENT IF EXISTS move_to_archive_category;
DELIMITER $$
CREATE EVENT move_to_archive_category
ON SCHEDULE EVERY 1 MINUTE STARTS '2015-09-01 00:00:00'
ON COMPLETION PRESERVE
DO
BEGIN
DECLARE incarnationId int default 0;
DECLARE evtAlias varchar(20);
SET evtAlias:='move_2_archive';
INSERT incarnations(usedBy) VALUES (evtAlias);
SELECT LAST_INSERT_ID() INTO incarnationId;
INSERT EvtsLog(incarnationId,evtName,step,debugMsg,dtWhenLogged)
SELECT incarnationId,evtAlias,1,'Event Fired, begin looking',now();
INSERT INTO `oc_product_to_category` (product_id, category_id)
SELECT product_id, 68 as category_id
FROM oc_product p WHERE p.event_end < NOW() AND p.event_end <> '0000-00-00';
-- perhaps collect metrics for above insert and use that in debugMsg below
-- perhaps with a CONCAT into a msg
INSERT EvtsLog(incarnationId,evtName,step,debugMsg,dtWhenLogged)
SELECT incarnationId,evtAlias,10,'INSERT finished',now();
-- pretend there is more stuff
-- ...
-- ...
INSERT EvtsLog(incarnationId,evtName,step,debugMsg,dtWhenLogged)
SELECT incarnationId,evtAlias,99,'Event Finished',now();
END $$
DELIMITER ;
Tables:
create table oc_product_to_category
( product_id INT not null,
category_id INT not null
);
create table oc_product
( product_id INT not null,
event_end datetime not null
);
drop table if exists incarnations;
create table incarnations
( -- NoteA
-- a control table used to feed incarnation id's to events that want performance reporting.
-- The long an short of it, insert a row here merely to acquire an auto_increment id
id int auto_increment primary key,
usedBy varchar(50) not null
-- could use other columns perhaps, like how used or a datetime
-- but mainly it feeds back an auto_increment
-- the usedBy column is like a dummy column just to be fed a last_insert_id()
-- but the insert has to insert something, so we use usedBy
);
drop table if exists EvtsLog;
create table EvtsLog
( id int auto_increment primary key,
incarnationId int not null, -- See NoteA (above)
evtName varchar(20) not null, -- allows for use of this table by multiple events
step int not null, -- facilitates reporting on event level performance
debugMsg varchar(1000) not null,
dtWhenLogged datetime not null
-- tweak this with whatever indexes your can bear to have
-- run maintenance on this table to rid it of unwanted rows periodically
-- as it impacts performance. So, dog the rows out to an archive table or whatever.
);
Turn on Events:
show variables where variable_name='event_scheduler'; -- OFF currently
SET GLOBAL event_scheduler = ON; -- turn her on
SHOW EVENTS in so_gibberish; -- confirm
Confirm Evt is firing:
SELECT * FROM EvtsLog WHERE step=1 ORDER BY id DESC; -- verify with our sonar
For more details of those helper tables, visit those links off my profile page for Events. Pretty much just the one link for Performance Tracking and Reporting.
You will also note that it is of no concern at the moment of having any data in the actual tables that you were originally focusing on. That can come later, and can be reported on in the evt log table by doing a custom string CONCAT into a string variable (for the counts etc). And reporting that in a step # like step 10 or 20.
The point is, you are completely blind without something like this as to know what is going on.
So,
I saw in mysqlog the following errors
160816 10:18:00 [ERROR] Event Scheduler: [root#localhost][events.move_to_archive_category] Duplicate entry '29-68' for key 'PRIMARY'
160816 10:18:00 [Note] Event Scheduler: [root#localhost].[events.move_to_archive_category] event execution failed.
and I just add INGORE in SQL INSERT... so the finally result is
INSERT IGNORE INTO `oc_product_to_category` (product_id, category_id)

How to display result and or any message in trigger body?

Hi I want to create trigger if it's condition satisfy then it's body should be executed and I want to display some message or any data that should be displayed if trigger body executed.
I want that if quantity of product went less then 50 then it should display message or some data.
Is it possible to display message ?
Here testdata is table name.
Code :
delimiter //
create trigger trigger2 before update on test.testdata
for each row
begin
if new.qty < 50 then
**display here some message that quantity is less**
end if;
end;
//
delimiter ;
You cannot do it, there is no place to output them in MySQL. As a work around you can add your message to the table, and then read this table.
Short example -
CREATE TABLE table1 (
column1 VARCHAR(255) DEFAULT NULL
);
CREATE TABLE messages (
id INT(11) NOT NULL AUTO_INCREMENT,
message VARCHAR(255) DEFAULT NULL,
time TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id)
);
DELIMITER $$
CREATE TRIGGER trigger1
AFTER INSERT
ON table1
FOR EACH ROW
BEGIN
INSERT INTO messages(message) VALUES ('new action');
END
$$
DELIMITER ;
Also, you could use UDF function write your logic.
More information - CREATE FUNCTION Syntax for User-Defined Functions.
For Quick and plain answering: You cannot Display Messages From Triggers. You may Only Throw errors.
You are propably not knowing the reason u use triggers in databases assuming from your question. We all have passed that level so dont worry. U have understood the syntax when we use triggers but not how and what they can do and not.
A trigger will do (for your case BEFORE an UPDATE) something concerning the database and only that.
That means the trigger cannot display any message on your screen. You can only handle database staff and not all of the actions are allowed for that too or some actions arent even recommended!.
That is for the theory part.
To give you a solution to your problem now.
The only thing you can do to know when the trigger has worked (that means when the new.qua<50) or basically check anything with any other trigger is the following. (Just a small fast solution):
You need to create a Table that will handle all logging of the
triggers.
Add in it an ID field, a descr field that will hold the action of
the triggerex. BefUpdate, BefInsert etc. another field for the
propably the condition that triggered the logging and antyhing else
u want displayed later in the application.
Then inside the if condition u are using write and insert
statemement to fill the info in the new (logging) table.
in your app later select that logging table to see the messages.
That is a useful and fast way to log, not only triggers but also functions (stored procs).
Judt for reference i give you s sample code with the CREATE, and the INSERT statement for your trigger.
CREATE TABLE LOGGER (
ID BIGINT PRIMARY KEY AUTO_INCREMENT,
DESCR_ACTIVITY VARCHAR(10),
ACTIVITY VARCHAR(50),
DT TIMESTAMP,
CONDITIONVALUE VARCHAR(50)
)
In the IF of your code now make it as :
if new.qty < 50 then
INSERT INTO LOGGER VALUES ('BEFINS','CHECKING QUA',NULL,'QUANTITY IS LOWER THAN 50')
end if;
And even from the workbench or from your application u can just :
SELECT * FROM LOGGER
to see the loggings.
But if i am confused from the reading and you want just to throw an error u can read the Mysql Documentation concerning throwing errors:
enter link description here
What u can do is in your if condition write something like:
if new.qty < 50 then
SIGNAL SQLSTATE '01000' SET MESSAGE_TEXT = 'Lower than 50', MYSQL_ERRNO = 1000;
endif;
What u should always NOT DO is alter the same table that a trigger is assigned and use only small portion of not so complex code in the trigger.
Hope i helped a bit.
Also, you can display any message using the select command.
IF (NEW.qty < 50) THEN
SELECT "message that quantity is less" AS Output;
END IF
Place above code inside the trigger. It will print the output

MySQL Trigger - Update relation table with extra values

What I'm trying to achieve is, I want to automate the values of the table between the users and folders table. Since it's a many-to-many relationship I created the user_folders table. Currently the server (nodejs) gets the request with userid, clientfolderid and some an array of bookmarks (which are not important now). It checks if the user already has this folder, by selecting from the user_folders table and if it's not existing it inserts a new row into the folder table. Then it has to send another statement to insert into the user_folders table.
So I have to "manually" keep the users_folder table updated.I guess this is a common problem and wanted to know if there is a pattern or a proven solution? The odd thing is that MySQL automatically handles the deletion of rows with an AFTER DELETE trigger but there is no (at least that I know of) automation with an AFTER INSERT trigger.
As I already said an AFTER INSERT trigger could possibly solve it, but I think it's not possible to pass some extra parameters to the AFTER INSERT trigger. This would be the user_id and the folder_client_id in my case.
I was thinking of a solution that I could create another table called tmp_folder which would look like:
tmp_folder
-- id
-- title
-- changed
-- user_id
-- folder_client_id
Then create an AFTER INSERT trigger on this table which inserts into folders and user_folders and then removes the row from tmp_folder again. Would this be the right way or is there a better one?
I would basically do the same with the bookmarks and user_bookmarks table. The best thing would be if it's even possible to insert a folder then the owner into the user_folders table with user_id and folder_client_id and then multiple other users into user_folders with the user_id and an default folder_client_id of -1 or something which will be updated later.
Meanwhile thanks for reading and I hope you can help me :)
PS: Is there a name for the table between 2 other tables in an m-2-m relationship?
I don't see an easy way to do this via triggers, but a stored procedure may suit you:
DELIMITER //
CREATE PROCEDURE
add_user_folder(
IN u_user_id BIGINT UNSIGNED,
IN u_folder_client_id BIGINT UNSIGNED,
IN v_title VARCHAR(255)
)
BEGIN
DECLARE u_found INT UNSIGNED DEFAULT 0;
SELECT
1 INTO u_found
FROM
user_folders
WHERE
user_id = u_user_id AND
folder_client_id = u_folder_client_id;
IF IFNULL(u_found, 0) = 0 THEN
START TRANSACTION;
INSERT INTO
folders
SET
title = v_title,
changed = UNIX_TIMESTAMP();
INSERT INTO
user_folders
SET
user_id = u_user_id,
folder_id = LAST_INSERT_ID(),
folder_client_id = u_folder_client_id;
COMMIT;
END IF;
END;
//

Call a Stored Procedure From a Stored Procedure and/or using COUNT

Ok, First off, I am not a mysql guru. Second, I did search, but saw nothing relevant related to mysql, and since my DB knowledge is limited, guessing syntactical differences between two different Database types just isn't in the cards.
I am trying to determine if a particular value already exists in a table before inserting a row. I've decided to go about this using two Stored procedures. The first:
CREATE PROCEDURE `nExists` ( n VARCHAR(255) ) BEGIN
SELECT COUNT(*) FROM (SELECT * FROM Users WHERE username=n) as T;
END
And The Second:
CREATE PROCEDURE `createUser` ( n VARCHAR(255) ) BEGIN
IF (nExists(n) = 0) THEN
INSERT INTO Users...
END IF;
END
So, as you can see, I'm attempting to call nExists from createUser. I get the error that no Function exists with the name nExists...because it's a stored procedure. I'm not clear on what the difference is, or why such a difference would be necessary, but I'm a Java dev, so maybe I'm missing some grand DB-related concept here.
Could you guys help me out by any chance?
Thanks
I'm not sure how it helped you, but...
why SELECT COUNT(*) FROM (SELECT * FROM Users WHERE username=n) and not just SELECT COUNT(*) FROM Users WHERE username=n?
Just make the user name (or whatever the primary application index is) a UNIQUE index and then there is no need to test: Just try to insert a new record. If it already exists, handle the error. If it succeeds, all is well.
It can (and should) all be one stored procedure.

Speeding up a MySQL stored routine

I was wondering if it was possible to speed up this MySQL query. Currently, I am calling it 20,000+ times, and it takes a while to run (a while being about 10 to 20 minutes).
Here is the basic table layout:
db1:
INT(11) id
VARCHAR(45) col1
VARCHAR(100) col2
VARCHAR(100) col3
db2:
VARCHAR(45) id
db3:
VARCHAR(45) fk_db2
INT(11) fk_db1
Here is the stored routine:
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `proc`(IN fk VARCHAR(45),
IN a VARCHAR(45),
IN b VARCHAR(100),
IN c VARCHAR(65))
BEGIN
SET #id=0;
SELECT id INTO #id FROM db1 WHERE db1.col1=a
AND db1.col2=b
AND db1.col3=c;
IF #id=0 THEN
INSERT INTO db1 (col1, col2, col3)
VALUES (a, b, c);
SELECT LAST_INSERT_ID() INTO #id;
END IF;
-- Association table for db2 and db1.
INSERT IGNORE INTO db3 (fk_db1, fk_db2)
VALUES(#id, fk);
END
The main point of this routine, is I want to get the ID of a specific record, or create one if it doesn't exist. Then I want to associate the fk passed in and the ID that I just found out. I'm sure there is a MySQL one-liner for this, but I have been unable to figure it out.
Any help is greatly appreciated!!
Thank you!
By the way, the names of columns are much better in the actual database, but I can't share the names with you all.
Actually, unless I'm missing something, that doesn't look all that slow. Can you define "a while" in "takes a while"?
Do you have an index on col1/col2/col3?
How exactly are you calling the procedure in your application? You are reusing the database connections, right?
Can you bundle multiple calls into a single transaction if you're not already?