How to update multiple tables and fields in a Trigger? - mysql

I have the following schema : SQL Fiddle.
The trigger is updating the articles.votes field. I also need it to update the members.points field and the members lifetime_points fields with the same formula as updates the articles.votes field. How do I do that?

Are you looking for this?
DELIMITER $$
CREATE TRIGGER tg_ai_article_votes
AFTER INSERT ON article_votes
FOR EACH ROW
BEGIN
UPDATE articles
SET votes = COALESCE(votes, 0) + 1
WHERE id = NEW.article_id;
UPDATE members
SET points = COALESCE(points, 0) + 1,
lifetime_points = COALESCE(lifetime_points, 0) + 1
WHERE id = NEW.member_id;
END$$
CREATE TRIGGER tg_ad_article_votes
AFTER DELETE ON article_votes
FOR EACH ROW
BEGIN
UPDATE articles
SET votes = COALESCE(votes, 0) - 1
WHERE id = OLD.article_id;
UPDATE members
SET points = COALESCE(points, 0) - 1,
lifetime_points = COALESCE(lifetime_points, 0) - 1
WHERE id = OLD.member_id;
END$$
DELIMITER ;
Here is SQLFiddle demo

Related

How do i create an INSERT trigger that based on a condition, either updates or inserts into another table?

I'm trying to create a trigger in phpmyadmin and I have 2 tables, Items and Inventory. This is for a Library Database. When i insert a new item, if the items isbn already exists in the inventory, i want to add to the totalCopies of that item in the inventory. If not, i want it to insert into inventory a new row with the new items isbn. This is my first time using triggers and I'm getting syntax errors. This is what i have right now.
BEGIN
IF ((SELECT COUNT(*) FROM inventory WHERE inventory.isbn = NEW.isbn) > 0) THEN
(
UPDATE inventory
SET inventory.totalCopies = inventory.totalCopies + 1
WHERE inventory.isbn = NEW.isbn
SET inventory.totalAvailable = inventory.totalAvailable + 1
WHERE inventory.isbn = NEW.isbn
)
ELSE
INSERT INTO inventory VALUES( , NEW.isbn, 1, 1, 0)
END
The inventory columns are : inventoryID (PrimaryKey)(autoincrement), isbn, totalCopies, totalAvailable, totalCheckedOut.
I assume for this trigger that inventory id is an auto_increment value,
or else the insert gives an error.
the UPDATE query can be a little simplifoed
DELIMITER $$
CREATE TRIGGER insorup
AFTER INSERT
ON ORDERS FOR EACH ROW
BEGIN
IF (EXISTS(SELECT 1 FROM inventory WHERE inventory.isbn = NEW.isbn) ) THEN
UPDATE inventory
SET inventory.totalCopies = inventory.totalCopies + 1 ,
inventory.totalAvailable = inventory.totalAvailable + 1
WHERE inventory.isbn = NEW.isbn;
ELSE
INSERT INTO inventory VALUES ( NEW.isbn, 1, 1, 0);
END IF;
END$$
DELIMITER ;
as you see in the comment, Shadow pointed out that it is also possible to reduce the inner Part. When you have the column ISBN declared as UNIQUE
DELIMITER $$
CREATE TRIGGER insorup
AFTER INSERT
ON ORDERS FOR EACH ROW
BEGIN
INSERT INTO
inventory VALUES ( NEW.isbn, 1, 1, 0)
ON DUPLICATE KEY UPDATE inventory.totalCopies = inventory.totalCopies + 1 ,
inventory.totalAvailable = inventory.totalAvailable + 1;
END$$
DELIMITER ;

Update Data When Trigger Execute in Mysql?

I want WaterRateMeterStart with WaterRateMeterEnd have same value sequentially if data updated.
This is my table
And This is my table structure
This my code and
I do not know how mistakes can occur.
DELIMITER $$
CREATE TRIGGER after_update_rates_water_again AFTER UPDATE ON waterrates FOR EACH ROW
BEGIN
IF
new.WaterRateMeterStart <> old.WaterRateMeterStart THEN
UPDATE waterrates
SET WaterRateMeterEnd = new.WaterRateMeterStart
WHERE
WaterRateId = ( new.WaterRateId - 1 );
ELSEIF new.WaterRateMeterEnd <> old.WaterRateMeterEnd THEN
UPDATE waterrates
SET WaterRateMeterStart = new.WaterRateMeterEnd
WHERE
WaterRateId = ( new.WaterRateId + 1 );
END IF;
END $$
DELIMITER ;
And error if I updated the data
UPDATE `waterrates` SET `WaterRateMeterStart` = '9' WHERE `waterrates`.`WaterRateId` = 2

trigger mysql unknown table

I've been trying to solve this problem. Here is the code:
CREATE TRIGGER some_trigger AFTER UPDATE ON table_a
FOR EACH ROW BEGIN
DECLARE tname VARCHAR(20);
IF (table_a.field_offer=1) THEN
SET tname='table_b';
ELSEIF (table_a.field_offer=0) THEN
SET tname='table_c';
END IF;
IF ((new.field_state = 0)) THEN
UPDATE tname join table_a on tname.ID=table_a.ref_field
SET tname.STOCK = tname.STOCK -1;
ELSEIF ((new.field_state = 1)) THEN
UPDATE tname join table_a on tname.ID=table_a.ref_field
SET tname.STOCK = tname.STOCK +1;
END IF;
END;
I am getting:
Unknown table 'table_a' in field list. Field_offer and field_state can
be null.
I've shown below what was said in the comments to the question:
CREATE TRIGGER some_trigger AFTER UPDATE ON table_a
FOR EACH ROW BEGIN
DECLARE tname VARCHAR(20);
IF (NEW.field_offer=1) THEN
UPDATE `table_b`
SET STOCK = CASE NEW.field_state
WHEN 0 THEN STOCK - 1
WHEN 1 THEN STOCK + 1
ELSE STOCK
END
WHERE ID=NEW.ref_field
;
ELSEIF (NEW.field_offer=0) THEN
UPDATE `table_c`
SET STOCK = CASE NEW.field_state
WHEN 0 THEN STOCK - 1
WHEN 1 THEN STOCK + 1
ELSE STOCK
END
WHERE ID=NEW.ref_field
;
END IF;
...
Note that I changed the updates from UPDATE ... JOIN as MySQL does not allow you to change the data of the triggered table; while you were not actually updating table_a, the JOIN could have been enough for MySQL to have objected... that and the joins would've updated every row in table_b/c that had a match in table_a, not just table_a rows related to the values in or row of the trigger.

MySQL TRIGGER with an IF statement determined by an IN

I'am constructing a trigger
CREATE TRIGGER `address_control_update` BEFORE UPDATE ON `orders_shipping`
FOR EACH ROW
BEGIN
SET #error_msg_id := (SELECT `error_msg_id` FROM `orders_err` WHERE orderID = OLD.orderID);
IF(LENGTH(NEW.ShippingName) = 0 OR NEW.ShippingName IS NULL) THEN
INSERT INTO `orders_err` (orderID,error_msg_id) VALUES(NEW.orderID,100) ON DUPLICATE KEY UPDATE complete=false;
ELSEIF (100 IN (SELECT #error_msg_id)) THEN
UPDATE `orders_err` SET complete = true WHERE orderID = OLD.orderID AND error_msg_id = 100;
END IF;
END;//
Where I would like to, first see if there is any error_msg_id on the orderID and later use this in the elseif statement to see if a possible error has been completed.
It works if i do this
IF(LENGTH(NEW.ShippingName) = 0 OR NEW.ShippingName IS NULL) THEN
INSERT INTO `orders_err` (orderID,error_msg_id) VALUES(NEW.orderID,100) ON DUPLICATE KEY UPDATE complete=false;
ELSEIF EXISTS(SELECT * FROM orders_err WHERE orderID = OLD.orderID AND error_msg_id = 100) THEN
UPDATE `orders_err` SET complete = true WHERE orderID = OLD.orderID AND error_msg_id = 100;
END IF;
But i would like to only call the select once if possible, since i have 12 similar if statements on other columns in the orders_shipping table.
On update i get the error: ERROR 1242: 1242: Subquery returns more than 1 row
Thanks in advance
One of possible solutions is to concatenate ids to one string separated by commas using GROUP_CONCAT(..) function - then first SELECT statement will return one row. To test whether an id is in the string use FIND_IN_SET(..) function:
CREATE TRIGGER `address_control_update` BEFORE UPDATE ON `orders_shipping`
FOR EACH ROW
BEGIN
SET #error_msg_ids = (
SELECT GROUP_CONCAT(DISTINCT `error_msg_id`)
FROM `orders_err`
WHERE orderID = OLD.orderID
GROUP BY orderID);
IF(LENGTH(NEW.ShippingName) = 0 OR NEW.ShippingName IS NULL) THEN
INSERT INTO `orders_err` (orderID,error_msg_id) VALUES(NEW.orderID,100) ON DUPLICATE KEY UPDATE complete=false;
ELSEIF (FIND_IN_SET(100, #error_msg_ids)) THEN
UPDATE `orders_err` SET complete = true WHERE orderID = OLD.orderID AND error_msg_id = 100;
END IF;
END;//
Also I think there exists other possibilities. For example you can write code without SELECT statement:
IF(LENGTH(NEW.ShippingName) = 0 OR NEW.ShippingName IS NULL) THEN
INSERT INTO `orders_err` (orderID,error_msg_id) VALUES(NEW.orderID,100) ON DUPLICATE KEY UPDATE complete=false;
ELSE
UPDATE `orders_err` SET complete = true WHERE orderID = OLD.orderID AND error_msg_id = 100;
END IF;
why - because you use the same filtering conditions in SELECT and in UPDATE - so there no need to test the same conditions before update.

Finding min and max value of the table in a constant time

I have a table which contains relative large data,
so that it takes too long for the statements below:
SELECT MIN(column) FROM table WHERE ...
SELECT MAX(column) FROM table WHERE ...
I tried index the column, but the performance still does not suffice my need.
I also thought of caching min and max value in another table by using trigger or event.
But my MySQL version is 5.0.51a which requires SUPER privilege for trigger and does not support event.
It is IMPOSSIBLE for me to have SUPER privilege or to upgrade MySQL.
(If possible, then no need to ask!)
How to solve this problem just inside MySQL?
That is, without the help of OS.
If your column is indexed, you should find min(column) near instantly, because that is the first value MySQL will find.
Same goes for max(column) on an indexed column.
If you cannot add an index for some reason the following triggers will cache the MIN and MAX value in a separate table.
Note that TRUE = 1 and FALSE = 0.
DELIMITER $$
CREATE TRIGGER ai_table1_each AFTER INSERT ON table1 FOR EACH ROW
BEGIN
UPDATE db_info i
SET i.minimum = LEAST(i.minimum, NEW.col)
,i.maximum = GREATEST(i.maximum, NEW.col)
,i.min_count = (i.min_count * (new.col < i.minumum))
+ (i.minimum = new.col) + (i.minimum < new.col)
,i.max_count = (i.max_count * (new.col > i.maximum))
+ (i.maximum = new.col) + (new.col > i.maximum)
WHERE i.tablename = 'table1';
END $$
CREATE TRIGGER ad_table1_each AFTER DELETE ON table1 FOR EACH ROW
BEGIN
DECLARE new_min_count INTEGER;
DECLARE new_max_count INTEGER;
UPDATE db_info i
SET i.min_count = i.min_count - (i.minimum = old.col)
,i.max_count = i.max_count - (i.maximum = old.col)
WHERE i.tablename = 'table1';
SELECT i.min_count INTO new_min_count, i.max_count INTO new_max_count
FROM db_info i
WHERE i.tablename = 'table1';
IF new_max_count = 0 THEN
UPDATE db_info i
CROSS JOIN (SELECT MAX(col) as new_max FROM table1) m
SET i.max_count = 1
,i.maximum = m.new_max;
END IF;
IF new_min_count = 0 THEN
UPDATE db_info i
CROSS JOIN (SELECT MIN(col) as new_min FROM table1) m
SET i.min_count = 1
,i.minimum = m.new_min;
END IF;
END $$
DELIMITER ;
The after update trigger will be some mix of the insert and delete triggers.