MySQL trigger - Adding from a separate table - mysql

I have two tables:
CREATE TABLE `class` (
`id` int(11) NOT NULL,
`class_no` int(11) NOT NULL,
`class_title` varchar(11) NOT NULL,
`no_of_students` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `enrolment` (
`id` int(11) NOT NULL,
`ssn` varchar(11) NOT NULL,
`class_no` int(11) NOT NULL,
`grade` varchar(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
What I am trying to do is when someone enters into 'Enrolment', if the class number entered matches an entry in 'Class', e.g. class number 2 - then i want what is typed into grade on 'Enrolment' (i.e 5) to add to whatever is already in number of students (Say if it's 10). In this case, the number of students would then say 15. This is the current trigger i have, however is not adding:
DELIMITER $$
CREATE TRIGGER `after_insert_add_one` AFTER INSERT ON `enrolment` FOR EACH ROW UPDATE class
SET class.no_of_students = class.no_of_students + enrolment.grade WHERE class_no = NEW.class_no
$$
DELIMITER ;
If anyone knows i'd be grateful for the help.

You must use the NEW key instead of the name of table enrolment, try this
CREATE TRIGGER `after_insert_enrolment` AFTER INSERT ON `enrolment`
FOR EACH ROW BEGIN
UPDATE class c
SET
c.no_of_students = c.no_of_students + NEW.grade
WHERE c.class_no = NEW.class_no;
END

You’re not making changes with SET class.no_of_students = class.no_of_students + enrolment.grade if you’re not using enrolment.class_no to compare using where clause. Update it with NEW in place of enrolment if you’re using it:
DELIMITER $
CREATE TRIGGER `after_insert_add_one` AFTER INSERT ON `enrolment` FOR EACH ROW UPDATE class
SET class.no_of_students = class.no_of_students + NEW.grade WHERE class_no = NEW.class_no
$
DELIMITER ;

Related

mysql trigger: after insert, +1 depending on what is entered?

CREATE TABLE `inventory` (
`id` int(11) NOT NULL,
`owner` int(11) DEFAULT NULL,
`grade1` int(11) DEFAULT NULL,
`grade2` int(11) NOT NULL,
`grade3` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
--
-- Dumping data for table `inventory`
--
INSERT INTO `inventory` (`id`, `owner`, `grade1`, `grade2`, `grade3`) VALUES
(3, 1, 2, 1, 1);
-- --------------------------------------------------------
--
-- Table structure for table `transfer`
--
CREATE TABLE `transfer` (
`id` int(11) NOT NULL,
`owner` int(11) DEFAULT NULL,
`total` char(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
--
-- Dumping data for table `transfer`
--
INSERT INTO `transfer` (`id`, `owner`, `total`) VALUES
(20, 1, 1);
--
-- Triggers `transfer`
--
DELIMITER $$
CREATE TRIGGER `t` AFTER INSERT ON `transfer` FOR EACH ROW update inventory t1
set t1.grade1 = t1.grade1 + 1
WHERE t1.owner = new.owner
AND `total` = '/1'
$$
DELIMITER ;
I have two tables as you can see from above code. I am in the process of using triggers in MySQL.
What I am trying to do, is that when someone enters something into transfer, and the owner matches the owner which is in the inventory- if what they have typed '(a number)/1' into total in transfer, it would add 1 to grade1. If they typed in '(a number)/2' into total, it will add 1 to grade2. And same for grade3. As you can see from the trigger above, this is what I have tried. I have tried it without the AND `total` = '/1' so I know the issue must be within that part. I have also tried without the ` around total, however it doesn't recognise this column without it.
I've had a look through SO and cannot find anything to resolve this.
I need this section done through a trigger- if anyone has any idea, can they please let me know. Thanks
The usual way to update different columns conditionally is to update all of them, but use a condition to determine whether to give them a new value or keep the old value. This can be used in a trigger just like any other UPDATE query.
CREATE TRIGGER `t` AFTER INSERT ON `transfer` FOR EACH ROW update inventory t1
set t1.grade1 = IF(new.total LIKE '%/1', t1.grade1 + 1, t1.grade1),
t1.grade2 = IF(new.total LIKE '%/2', t1.grade2 + 1, t1.grade2),
t1.grade3 = IF(new.total LIKE '%/3', t1.grade3 + 1, t1.grade3)
WHERE t1.owner = new.owner
It seems like what you really need is a fourth column grade in the transfer table so your trigger code can know which grade to increment. All you have in the trigger is OLD.* and NEW.*, the columns of the row that changed. You can't make one of your current integer columns carry extra information that is more than a simple integer.
ALTER TABLE transfer ADD COLUMN grade TINYINT UNSIGNED;
Then you can use this in the trigger to tell which grade to increment.
CREATE TRIGGER `t` AFTER INSERT ON `transfer` FOR EACH ROW
UPDATE inventory
SET grade1 = grade1 + CASE NEW.grade WHEN 1 THEN 1 ELSE 0 END,
grade2 = grade2 + CASE NEW.grade WHEN 2 THEN 1 ELSE 0 END,
grade3 = grade3 + CASE NEW.grade WHEN 3 THEN 1 ELSE 0 END;
WHERE owner = NEW.owner

Update Column in Another Table Based off INT value Change using MySQL AFTER UPDATE Trigger

Getting error after error. Basically I am trying to set a columns value to 1 in my products table automatically if upon update of the product_stock table the column available is greater than 0 (meaning, at least one in stock).
MPN is both a unique and foreign key in my products table, so as long a positive value in the column available in the table product_stock the in_stock value for the mpn in the products table should be set to 1.
Two tables I'm working with:
1
CREATE TABLE `products` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`mpn` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`in_stock` int(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `mpn` (`mpn`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
2
CREATE TABLE `product_stock` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`mpn` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`size` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`available` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `product_stock_ibfk_1` (`mpn`),
CONSTRAINT `product_stock_ibfk_1` FOREIGN KEY (`mpn`) REFERENCES `products` (`mpn`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
And one variation of my trigger
DELIMITER $$
CREATE TRIGGER ps_update AFTER UPDATE ON `product_stock`
FOR EACH ROW BEGIN
IF NEW.available > 0 THEN
SET products.in_stock = 1;
ELSE
SET products.in_stock = 0;
END IF;
END$$
DELIMITER ;
Error code
1193 - Unknown system variable 'in_stock'
You cannot update value in another table using SET alone. You need to use proper UPDATE statement to do so.
I have also added more conditions, so that it does not fire UPDATE query every time. It will fire UPDATE only when there is a change in the in_stock value required.
DELIMITER $$
CREATE TRIGGER ps_update AFTER UPDATE ON `product_stock`
FOR EACH ROW BEGIN
-- update only when there is a change in the available
IF NEW.available <> OLD.available THEN
-- update only when item becomes in_stock
IF NEW.available > 0 AND OLD.available <= 0 THEN
UPDATE products
SET products.in_stock = 1
WHERE products.mpn = NEW.mpn;
-- update only when item becomes out_stock
ELSEIF NEW.available <= 0 AND OLD.available > 0 THEN
UPDATE products
SET products.in_stock = 0
WHERE products.mpn = NEW.mpn;
END IF;
END IF;
END $$
DELIMITER ;

Update another table if conditions are met

as I'm working on a small app for managing metadata and I was wondering if it is possible to insert row in another table if conditions are met.
Let me follow with example: So, let's say we have table ispu_plan
CREATE TABLE `ispu_plan` (
`id` int(11) NOT NULL,
`id_jls` int(11) NOT NULL,
`id_razina_plan` int(11) NOT NULL,
`id_revizija` int(11) NOT NULL,
`naziv_plan` varchar(150) NOT NULL,
`ispu_naziv` varchar(100) NOT NULL,
`id_mjerilo` int(11) NOT NULL,
`datum_donosenja_plana` date DEFAULT NULL,
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
and as I'm updating table ispu_plan I want to update another table (e.g. ispu_plan_updated) if certain conditions are met in ispu_plan with same rows from table ispu_plan
Using this query:
SELECT * FROM ispu_plan WHERE datum_donosenja_plana BETWEEN '2014-01-01' AND CURDATE()
I want to insert row in table ispu_plan_updated. Is something like this possible and can I insert rows in ispu_plan_updated using views?
Thank you
You can use a trigger to achieve that:
DROP TRIGGER IF EXISTS ispu_plan_trigger;
DELIMITER |
CREATE TRIGGER ispu_plan_trigger AFTER UPDATE ON ispu_plan
FOR EACH ROW BEGIN
-- example condition with update:
IF NEW.datum_donosenja_plana >= '2017-01-01' THEN
UPDATE ispu_plan_updated SET naziv_plan = 'some_value' WHERE id = NEW.id
LIMIT 1;
END IF;
END;
DELIMITER ;

Mysql Trigger will not pass last_insert_id() to connection

This is my schema:
I am trying to have an insert into "desktops" or "laptops" insert an id generated automatically from "computers". That works.
My issue is when I insert into either table, I can not select last_insert_id();
Is there something I am doing wrong? I am trying to pass the id all the way forward to my application, for further processing. Selecting MAX(id) is not a valid solution. My SQL connection makes one insert statement, and the trigger should not break that functionality...
Use test;
CREATE TABLE `laptops` (
`id` int(11) NOT NULL,
`name` varchar(45) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=innodb DEFAULT CHARSET=utf8;
CREATE TABLE `desktops` (
`id` int(11) NOT NULL,
`name` varchar(45) DEFAULT NULL,
PRIMARY KEY (`id`),
) ENGINE=innodb DEFAULT CHARSET=utf8;
CREATE TABLE `computers` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`type` varchar(45) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=innodb DEFAULT CHARSET=utf8;
CREATE TRIGGER `laptops_BINS` BEFORE INSERT ON `laptops` FOR EACH ROW
BEGIN
IF (EXISTS(SELECT id FROM laptops WHERE name = NEW.name)) THEN
SET NEW.id = NULL;
ELSE
INSERT INTO computers (type) VALUES ('laptop');
SET NEW.id = LAST_INSERT_ID();
SET NEW.id = LAST_INSERT_ID(NEW.id);
END IF;
END
CREATE TRIGGER `desktop_BINS` BEFORE INSERT ON `desktops` FOR EACH ROW
BEGIN
IF (EXISTS(SELECT id FROM desktops WHERE name = NEW.name)) THEN
SET NEW.id = NULL;
ELSE
INSERT INTO computers (type) VALUES ('desktop');
SET NEW.id = LAST_INSERT_ID();
SET NEW.id = LAST_INSERT_ID(NEW.id);
END IF;
END
INSERT INTO laptops (name) VALUES ('laptop1');
INSERT INTO laptops (desktop) VALUES ('desktop1');
INSERT INTO laptops (name) VALUES ('laptop2');
INSERT INTO laptops (desktop) VALUES ('desktop2');
SELECT last_insert_id();
Expecting 4, actually its 0.
Any thoughts as to how I can fix the trigger? Maybe someone can help me format the AFTER_INSERT statement to fix last_insert_id?
I tried setting the values to auto-increment, and unique in the laptops and desktops table, neither will fix the issue.
Rather than trying to deal with the 'confusion' of 'last_insert_id'. I decided to change the table structure to be a more 'common' format.
That is change the 'laptops' and 'desktops' tables to have the 'auto_increment' keys. This changes the 'computers' table to have a primary key of 'computer_id' from 'laptops' or 'desktops' and a 'computer_type'.
Here are the table structures and triggers.
It has been tested on mysql 5.5.16 on windows xp.
CREATE TABLE `laptops` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(45) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
CREATE TABLE `desktops` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(45) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
CREATE TABLE `computers` (
`computer_id` int(11) NOT NULL,
`computer_type` varchar(45) NOT NULL,
PRIMARY KEY (`computer_id`,`computer_type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DELIMITER $$
USE `testmysql`$$
DROP TRIGGER /*!50032 IF EXISTS */ `laptop_bins`$$
CREATE
/*!50017 DEFINER = 'test'#'localhost' */
TRIGGER `laptop_bins` AFTER INSERT ON `laptops`
FOR EACH ROW BEGIN
INSERT INTO computers (computer_id, computer_type ) VALUES (new.id, 'laptop');
END;
$$
DELIMITER ;
DELIMITER $$
USE `testmysql`$$
DROP TRIGGER /*!50032 IF EXISTS */ `desktop_bins`$$
CREATE
/*!50017 DEFINER = 'test'#'localhost' */
TRIGGER `desktop_bins` AFTER INSERT ON `desktops`
FOR EACH ROW BEGIN
INSERT INTO computers (computer_id, computer_type ) VALUES (new.id, 'desktop');
END;
$$
DELIMITER ;
Sample Queries and Output:
INSERT INTO laptops (NAME) VALUES ('laptop1');
INSERT INTO desktops (NAME) VALUES ('desktop1');
INSERT INTO laptops (NAME) VALUES ('laptop2');
INSERT INTO desktops (NAME) VALUES ('desktop2');
Laptops:
id name
------ ---------
1 laptop1
2 laptop2
Desktops:
id name
------ ----------
1 desktop1
2 desktop2
Computers:
computer_id computer_type
----------- ---------------
1 desktop
1 laptop
2 desktop
2 laptop
This more a possible approach to the requirement than an answer.
I can create the code if required. It is not a lot of code on top of what is here.
The problem is to maintain tables in an other database, in sync, without doing lots of repeat work.
My suggestion:
In the 'computers' database - have a 'computers_new' table that is inserted to by the 'after insert' trigger and holds the relevant key information. Including a 'unprocessed' column.
I would then run a script at regular intervals or was triggered when the 'computers_new' table changed. It would:
1) transfer the 'unprocessed' details to the 'laptops', 'desktops' tables in the other database.
2) mark the transferred records as processed.
Advantages:
Lots of small chunks of work.
By using transactions it is reliable.
Drawbacks.
Ensuring tables are in sync.

Generate auto incremented id for BPM application

Within a BPM web application, I have a field for an invoice # on a particular page but I need for it to be auto generated every time a user attaches an invoice and views that page. That number must be unique and preferably auto-incremented. A value for the invoice # field can be displayed by querying from a table from an external MYSQL database. So every time a user lands on that particular page, a SELECT query statement can be fired.
On MYSQL end, how would I set this up? So basically, I would like to setup a query for that invoice # field where it will for run a query for example,
SELECT invoice_num FROM invoice_generator
and every time this query runs, it would return the next incremented number.
You can use mysql trigger concept here....
I have added one example here...
It will be very usefull for u (see this link also :http://www.freemindsystems.com/mysql-triggers-a-practical-example/)
CREATE TABLE `products` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL DEFAULT '',
`price` int(20) NOT NULL DEFAULT '0',
`other` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `products_name_idx` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `freetags` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`tag` varchar(50) NOT NULL DEFAULT '',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `freetagged_objects` (
`tag_id` int(20) NOT NULL DEFAULT '0',
`object_id` int(20) NOT NULL DEFAULT '0',
`tagged_on` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`module` varchar(50) NOT NULL DEFAULT '',
PRIMARY KEY (`tag_id`, `object_id`),
KEY `freetagged_objects_tag_id_object_id_idx` (`tag_id`, `object_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
INSERT_PRODUCTS_TAGS
DELIMITER ||
DROP TRIGGER IF EXISTS insert_products_tags;
||
DELIMITER ##
CREATE TRIGGER insert_products_tags AFTER INSERT ON products
FOR EACH ROW
BEGIN
DECLARE current_id integer;
DECLARE tag_id integer;
DECLARE next integer;
DECLARE tag_field varchar(255);
DECLARE next_sep integer;
DECLARE current_tag varchar(255);
DECLARE right_tag varchar(255);
-- We use the field other as comma-separated tag_field
SET tag_field = NEW.other;
-- Check for empty tags
IF (CHAR_LENGTH(tag_field) <> 0) THEN
-- Loop until no more ocurrencies
set next = 1;
WHILE next = 1 DO
-- Find possition of the next ","
SELECT INSTR(tag_field, ',') INTO next_sep;
IF (next_sep > 0) THEN
SELECT SUBSTR(tag_field, 1, next_sep - 1) INTO current_tag;
SELECT SUBSTR(tag_field, next_sep + 1, CHAR_LENGTH(tag_field)) INTO right_tag;
set tag_field = right_tag;
ELSE
set next = 0;
set current_tag = tag_field;
END IF;
-- Drop spaces between comas
SELECT TRIM(current_tag) INTO current_tag;
-- Insert the tag if not already present
IF (NOT EXISTS (SELECT tag FROM freetags WHERE tag = current_tag)) THEN
-- Insert the tag
INSERT INTO freetags (tag) values (current_tag);
SELECT LAST_INSERT_ID() INTO tag_id;
ELSE
-- Or get the id
SELECT id FROM freetags WHERE tag = current_tag INTO tag_id;
END IF;
-- Link the object tagged with the tag
INSERT INTO freetagged_objects
(tag_id, object_id, module)
values
(tag_id, NEW.id, 'products');
END WHILE;
END IF;
END;
##
Now If you execute an insert on products table:
INSERT INTO PRODUCTS
(name, price, other)
values
("product1", 2, "tag1, tag2,tag3 , tag 4");