EVENT not executing as per the code in MySQL - mysql

I have created an event which will update the balance in table 'try_event' after every 5 minutes according to the balance and time at which the row is inserted.
EVENT code:
delimiter $$
CREATE EVENT `event`
ON SCHEDULE every 1 second
DO
begin
-- update balance by 2%
UPDATE try_event
SET Balance = case
WHEN timestampdiff(minute,date_created,current_timestamp) >0 and timestampdiff(minute,date_created,current_timestamp) MOD 5 = 0 and (balance>2000 and balance<3000) then Balance * 1.02
end;
end $$
delimiter ;
TABLE :
create table try_event(balance numeric(10,2) not null,date_created timestamp);
INSERTED ROWS:
insert into try_event values(2500,default);
insert into try_event values(1000,default);
but still it is giving the balance=2500 after 5 minutes.
When I remove "(balance>2000 and balance<3000)" the whole balance column is updated and result is:
2550
1020

I just created a test table on an instance of MySQL 5.5.29 (actually Percona Server, which is MySQL with some patches and new feature). It worked fine, it updated the 2500 balance and did not update the 1000 balance.
Here's a check that shows that the update happened (including automatically updating the TIMESTAMP column):
mysql> SELECT timestampdiff(MINUTE, date_created, current_timestamp) AS td,
balance FROM try_event;
+------+---------+
| td | balance |
+------+---------+
| 8 | 1000 |
| 3 | 2550 |
+------+---------+
I made slight changes to the event definition:
When using CASE expressions, it's worthwhile to represent an "ELSE" clause because otherwise if the WHEN condition is false, and you have no ELSE clause, the result of the CASE expression is just NULL.
UPDATE try_event
SET Balance = CASE
WHEN timestampdiff(minute,date_created,current_timestamp) >0
AND timestampdiff(minute,date_created,current_timestamp) MOD 5 = 0
AND balance>2000 and balance<3000
THEN Balance * 1.02
ELSE Balance -- this is what I added.
END;
Is it possible your balance column is declared NOT NULL, so the UPDATE is trying to set balance to NULL and it's simply failing?
I also remove the parentheses around the balance range comparison, but that shouldn't make any difference.

Related

SQL Trigger to Update a Record instead of Inserting if values havent changed

Im trying to implement an SQL Trigger that will UPDATE instead of INSERTing if the value of one column hasent change..
Example Table
Id | Name | Income | Date
11 John 10000 2019-02-01
11 John 15000 2019-02-02
This table does not have a Primary Key, because it will have multiple records for the same ID, each one with a diferent date. What I want to achive to save some space is to UPDATE instead of INSERT if the Income hasent changed... for example, if the next record is going to be:
11 John 15000 2019-02-03
I want the trigger to UPDATE the DATE instead of creating a new Record on the Table. So it will end up like:
11 John 10000 2019-02-01
11 John 15000 2019-02-03 <-- Changed!
The data comes from an hourly table that records the incomes from the day... and at the end of the day does a massive insert of all records on the hourly table to the incomes table to keep the history.
I tried the following trigger but it did not work since it does a lock.
DELIMITER //
CREATE TRIGGER before_insert_income
BEFORE INSERT ON income_30
FOR EACH ROW
BEGIN
IF NEW.income = (select income from income_30 where ID=NEW.ID and date=(select max(date) from income_30)) THEN
UPDATE income_30 set date = NEW.date where ID=NEW.ID;
END IF;
END; //
DELIMITER ;
I also though about doing an UPDATE instead of INSERT and perform a trigger that will do the opposite, will insert a new record if the income changed, but I didn't know which column could I make primary for the update.
I Apologise if it is a silly question, but its being bothering me for a while.
Thanks a lot!
If you only care about saving space I think you should only delete last month with the same income record just before you try to insert new income, with same amount.
DELIMITER //
CREATE TRIGGER before_insert_income
BEFORE INSERT ON income_30
FOR EACH ROW
BEGIN
IF NEW.income = (select income from income_30 where ID=NEW.ID and date=(select max(date) from income_30)) THEN
DELETE FROM income_30 where id=NEW.id and date=(select max(date) from income_30);
END IF;
END; //
DELIMITER;

how to execute a trigger everyday at once in sql

we are doing a project on microbanking and we need to calculate the interest rate in database level. So we used procedures and triggers for that. but the trigger needs to execute everyday at once to perform below given procedure. Can you provide a solution to create that trigger. Thanks in advance
DELIMITER //
create procedure addFDInterestToAccount(in account_number bigint(20))
BEGIN
declare interest float default 0;
declare account_type varchar(10);
declare rate float;
declare savings_account bigint(20);
start transaction;
select balance from fd_account where account_no = account_number into interest;
SELECT plan_id from fd_account where account_no = account_number into account_type;
SELECT saving_account_no from fd_account where account_no = account_number into savings_account;
select interest_rate from fd_plan where plan_id = account_type into rate;
set interest = interest*rate/1200;
insert into transaction (transaction_id, account_no, credit_debit, date_time, amount, type_, agent_id, is_fee) values (null, savings_account, 'debit', now(), interest, 'not_special', null, false);
update account set balance = balance + interest where account_no = savings_account;
commit;
END //
DELIMITER ;
call addFDInterestToAccount(90842311);
As far as I understood your requirement, I think you can use SQL Events for this. The general syntax goes like this,
CREATE [OR REPLACE]
[DEFINER = { user | CURRENT_USER | role | CURRENT_ROLE }]
EVENT
[IF NOT EXISTS]
event_name
ON SCHEDULE schedule
[ON COMPLETION [NOT] PRESERVE]
[ENABLE | DISABLE | DISABLE ON SLAVE]
[COMMENT 'comment']
DO sql_statement;
schedule:
AT timestamp [+ INTERVAL interval] ...
| EVERY interval
[STARTS timestamp [+ INTERVAL interval] ...]
[ENDS timestamp [+ INTERVAL interval] ...]
interval:
quantity {YEAR | QUARTER | MONTH | DAY | HOUR | MINUTE |
WEEK | SECOND | YEAR_MONTH | DAY_HOUR | DAY_MINUTE |
DAY_SECOND | HOUR_MINUTE | HOUR_SECOND | MINUTE_SECOND}
You can modify/use following code for achiving this,
CREATE EVENT example
ON SCHEDULE EVERY 1 DAY
STARTS CURRENT_TIMESTAMP
DO call addFDInterestToAccount(90842311);
For further information you can vist the link here
Also make sure you run this query before running the event
SET GLOBAL event_scheduler = ON;
A trigger is defined as a procedure that runs in response to an insert, update, or delete statement. It will run every time you perform one of those DML operations. You cannot schedule a trigger to run once a day.
From the MySql documentation (but applicable to all databases that implement triggers):
A trigger is defined to activate when a statement inserts, updates, or
deletes rows in the associated table. These row operations are trigger
events.
Instead, you need to look for a task scheduler. Each OS will have its own or you could find a third-party scheduler software.

MySQL - what happends when update a column by rand() or by now()?

I have a table:
CREATE TABLE `test` (
`t` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
and update it by:
update test set t = rand();
then the data in the table is:
+---------------------+
| t |
+---------------------+
| 0.24891147599454175 |
| 0.5710943421079725 |
| 0.10873731328988198 |
+---------------------+
as you see, every 't' is different
when update the table using:
update test set t = now(6);
and every 't' is the same:
+----------------------------+
| t |
+----------------------------+
| 2018-03-13 23:40:40.380817 |
| 2018-03-13 23:40:40.380817 |
| 2018-03-13 23:40:40.380817 |
+----------------------------+
what happends when update a column by rand() or by now()?
RAND() is recalculated per row. While NOW() will allways be the same value anywhere in the same statement. That all has nothing to do with UPDATE. It works the same with SELECT. Try the follwing:
select rand()
from (select 1 union all select 1 union all select 1) x;
select now(6)
from (select 1 union all select 1 union all select 1) x;
You will see the same effect.
Demo: http://rextester.com/JGJWV3591
The now() function returns the time when the statement begin to execute and is not recalled for every row while RAND() is recalled for every row. From the MYSQL documentation:
NOW() returns a constant time that indicates the time at which the
statement began to execute. (Within a stored function or trigger,
NOW() returns the time at which the function or triggering statement
began to execute.) This differs from the behavior for SYSDATE(), which
returns the exact time at which it executes.
so you can use SYSDATE() instead of NOW(). This way every row will be different
update test set t = SYSDATE();
The documentation for MySQL functions rand() and now() explain how those functions work, so I won't repeat much of that here. For reference:
https://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html#function_rand
https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_now
When you run the first statement with rand(), you get random floating point numbers greater than or equal to 0 and less than 1. When you run the second statement:
update test set t = now(6);
The seconds value is represented with a precision of 6 decimal places, as specified. The overall value returned represents the time at which the statement began to execute. That's why all the values are identical.

MySQL Trigger Sum Two Columns where userID is the same

I have the following product table
id | companyID | prodID | price | stock | stockAvailable | sumStock
1 A 2 10 5 4
2 B 2 50 10 5
I need to have a trigger when I update a product row that will update the sumStock.
I am new to Triggers, my attempt failed:
CREATE TRIGGER `SalesDB`.`stockSumUpdate` BEFORE UPDATE
ON SalesDB.Product FOR EACH ROW
BEGIN
SET Product.sumStock = Product.stock + Product.stockAvailable
END
My Goal is in this case to calculate the SUM(stock) AS stockSum Where ProductID=2
in this case that would be 15 and then add that to the sumStock. Then add the stockAvailable column to that as well. So in sumStock for both collumns I would have 24.
Result would be:
id | companyID | prodID | price | stock | stockAvailable | sumStock
1 A 2 10 5 4 29
2 B 2 50 10 5 29
Try this syntax:
CREATE TRIGGER `SalesDB`.`stockSumUpdate` BEFORE UPDATE
ON SalesDB.Product FOR EACH ROW
BEGIN
SET NEW.sumStock = NEW.stock + NEW.stockAvailable
END;
That adds within a row of a table.
To get the total, you would use something like:
CREATE TRIGGER `SalesDB`.`stockSumUpdate` BEFORE UPDATE
ON SalesDB.Product FOR EACH ROW
BEGIN
SET NEW.sumStock = (select sum(stock + stockAvailable)
from products where p.prodid = new.prodid and p.id <> id
) + NEW.stock + NEW.stockAvailable;
END;
Except, this still, doesn't do what you want. If you are updating multiple rows at a time, you will get different total stock values.
In other words, you are trying to update a group of rows whenever you update a single row. Gosh, when put like that, it doesn't seem like a good idea. The second set of udpates could update even more rows and so on and so on (it wouldn't happen because of the product).
Instead, create a table with product as a primary key and the stock available in that table (might be an existing table). Then update the summary table every time there is a change in this table.

Move rows from TableA into Table-Archive

Is it possible to move rows that are 3 days old into an other table called "Table_Archive" automatically in mysql ones a week?
tableA ex:
ID | stringvalue | Timestamp
1 | abc | 2011-10-01
2 | abc2 | 2011-10-02
3 | abc3 | 2011-10-05
4 | abc4 | 2011-10-10
5 | abc5 | 2011-10-11
After the move
tableA:
ID | stringvalue | Timestamp
4 | abc4 | 2011-10-10
5 | abc5 | 2011-10-11
Table_Archive:
ID | stringvalue | Timestamp
1 | abc | 2011-10-01
2 | abc2 | 2011-10-02
3 | abc3 | 2011-10-05
And when new input comes into tableA it wont be any problems with ID (PK) in the next move?
What Ive got:
CREATE PROCEDURE clean_tables ()
BEGIN
BEGIN TRANSACTION;
DECLARE _now DATETIME;
SET _now := NOW();
INSERT
INTO Table_Archive
SELECT *
FROM TableA
WHERE timestamp < _now - 3;
FOR UPDATE;
DELETE
FROM TableA
WHERE timestamp < _now - 3;
COMMIT;
END
How do I change _now to be the date 3 days ago?
Personally, I would make use of the MySQL Event Scheduler. This is a built in event scheduler rather like CRON in Linux.
You can specify it to call a procedure, procedures or functions or run a bit of SQL at designated intervals.
Read the MySQL docs but an example would be:
CREATE EVENT mydatabase.myevent
ON SCHEDULE EVERY 1 WEEK STARTS CURRENT_TIMESTAMP + INTERVAL 10 MINUTE
DO
call clean_tables();
So this is saying "call clean_tables() once a week and make the first call in 10 minutes' time"
One gotcha is that the event scheduler is (I think) disabled by default. To turn it on run:
SET GLOBAL event_scheduler = ON;
You can then run:
SHOW PROCESSLIST;
To see whether the event scheduler thread is running.
As for preserving your Table A ID column (if you must). I would keep the ID on Table_Archive as unique to that table i.e make it the primary key & auto_increment and then have a 'Original_TableA_ID' column in which to store the TableA ID. You can put a unique index on this if you want.
So Table_Archive would be like:
create table `Table_Archive` (
ID int unsigned primary key auto_increment, -- < primary key auto increment
tableAId unsigned int not null, -- < id column from TableA
stringValue varchar(100),
timestamp datetime,
UNIQUE KEY `archiveUidx1` (`tableAId`) -- < maintain uniqueness of TableA.ID column in Archive table
);
Nobody seems to have answered your original question "How do I change _now to be the date 3 days ago?". You do that using INTERVAL:
DELIMITER $
CREATE PROCEDURE clean_tables ()
BEGIN
BEGIN TRANSACTION;
DECLARE _now DATETIME;
SET _now := NOW();
INSERT
INTO Table_Archive
SELECT *
FROM TableA
WHERE timestamp < _now - interval 3 day;
FOR UPDATE;
DELETE
FROM TableA
WHERE timestamp < _now - interval 3 day;
COMMIT;
END$
DELIMITER ;
One final point is that you should consider creating an index on the timestamp column on TableA to improve the performance of you clean_tables() procedure.
You may need to have a look into cron jobs if you want that script/query to be executed automatically.
If you are using cpanel have a look into http://www.siteground.com/tutorials/cpanel/cron_jobs.htm
Adding to the best answer (imo) by Tom Mac regarding the event scheduler - be aware that when backing up the schema, you have to specify that you want the events backed up with it via the --events=TRUE flag.
If you're exporting manually in the workbench, the latest version has a checkbox on the main 'Export To Disk' tab - older versions hide it away in the Advanced Export Options tab.
It is possible, MySQL will execute query automatically at specific time using MySQL Event Scheduler. Check this link for more details.
https://dev.mysql.com/doc/refman/5.7/en/event-scheduler.html