Let's say we got two tables.
First table is items - id, title.
Second table is history - id, title, action, user.
We can have following AFTER INSERT trigger for "This user inserted this item":
INSERT INTO history (title, action, user) VALUES (NEW.title, 'INSERT', #phpUserId);
If I want to insert new item, I can do something like this.
SET #phpUserId = 123;
INSERT INTO items (title) VALUES ('My best item');
In this case, trigger works perfectly.
But the problem is, when I add some text into variable - for example SET #phpUserId = "library123"; - In this moment the trigger is not able to take that variable.
Any ideas why only integer variables are passed?
Good news there's nothing wrong with your trigger and here's the proof
drop table if exists i,h;
create table i(id int, title varchar(20));
create table h(id int, title varchar(20), action varchar(20), user varchar(30));
drop trigger if exists t;
delimiter $$
create trigger t after insert on i
for each row
begin
INSERT INTO h (title, action, user) VALUES (NEW.title, 'INSERT', #phpUserId);
end $$
delimiter ;
SET #phpUserId = 123;
INSERT INTO i (title) VALUES ('My best item');
SET #phpUserId = 'bob123';
INSERT INTO i (title) VALUES ('My worst item');
+------+---------------+--------+--------+
| id | title | action | user |
+------+---------------+--------+--------+
| NULL | My best item | INSERT | 123 |
| NULL | My worst item | INSERT | bob123 |
+------+---------------+--------+--------+
2 rows in set (0.00 sec)
Related
So my parent table, which is a lookup table shares an attribute with my child table (foreign key). So my goal is to have an after_delete trigger, or any trigger that will update the foreign key in the child table when a row in a parent table is deleted, without having to delete the whole row containing information in the child table. For example, I have a table named Status with the the attributes StatusID and Status. The statuses are WORKING and BROKEN. Then I have a Laptops Table that has the attributes LaptopID, LaptopName, and StatusID (Foreign Key).
For my Status Table, I have:
| 1 | WORKING |
| 2 | BROKEN |
Now Let's say I have a record:
1001 | Lenovo 17XS | 1 |
1002 | DELL XPS | 2 |
My goal is when I delete the second row of my status table, I want the second row of my Laptops table to say:
1002 | DELL XPS | DELETED |
I have turned cascade delete off for the foreign key so it does not delete the whole record.
This is my code for my attempt:
CREATE DEFINER=`TEST`#`%` TRIGGER `status_AFTER_DELETE` AFTER DELETE ON `status` FOR EACH ROW BEGIN
UPDATE Laptops INNER JOIN Status ON Laptops.StatusID = Status.StatusID
Set Laptops.StatusID = 0 WHERE Status.StatusID = Laptops.StatusID;
END
However, the trigger is not working.
I have added a record called "DELETED" with a StatusID 0 afterwards but the trigger is still not working. What am I doing wrong?
An after update trigger is too late, you could do this in a before trigger and you should reference OLD. values (see manual for details) nb debugging triggers is a pain and I usually write to a debug_table when I get into difficulty. eg
DROP TABLE IF EXISTS laptops;
drop table if exists status;
drop trigger if exists t;
create table status(statusid int primary key, val varchar(20));
create table laptops( LaptopID int, LaptopName varchar(20), StatusID int,
foreign key fk1(statusid) references status(statusid)) ;
insert into status values
(1,'working'),(2,'broken'),(0,'deleted');
insert into laptops values
(1001 , 'Lenovo 17XS', 1 ),
(1002 , 'DELL XPS' , 2 );
delimiter $$
CREATE TRIGGER `status_AFTER_DELETE` before DELETE ON `status`
FOR EACH ROW
BEGIN
insert into debug_table(msg) values ('fired');
UPDATE Laptops
Set Laptops.StatusID = 0 WHERE old.StatusID = Laptops.StatusID;
END $$
delimiter ;
truncate table debug_table;
delete from status where statusid = 2;
select * from status;
select * from debug_table;
I have some simple triggers on a table which work for to audit changes to a record by logging to a table any time an INSERT, UPDATE, or DELETE occurs. When a record is inserted into Users, a copy is made in Users_History with a Status field populated with the value 'INSERT'. Same is done for UPDATE and DELETE.
DELIMITER $$
CREATE TRIGGER Users_History_Insert AFTER INSERT ON Audit
FOR EACH ROW BEGIN
INSERT INTO Users_History select *, 'INSERT' from Users where ID = NEW.ID;
END$$
DELIMITER ;
DELIMITER $$
CREATE TRIGGER Users_History_Delete BEFORE DELETE ON Audit
FOR EACH ROW BEGIN
INSERT INTO Users_History select *, 'DELETE' from Users where ID = OLD.ID;
END$$
DELIMITER ;
DELIMITER $$
CREATE TRIGGER Users_History_Update AFTER UPDATE ON Audit
FOR EACH ROW BEGIN
INSERT INTO Users_History select *, 'UPDATE' from Users where ID = NEW.ID;
END$$
DELIMITER ;
We have a function where two Users records can be merged with each other. Effectively, all values of one record are overwritten with the values of another record, and the unchanged record is deleted.
If you had a table such as this:
| ID | Name | email |
| 1 | Billy | bill#mail.com |
| 2 | Bill | bill2#mail.com |
And I wanted to merge record 2 into 1, I'd end up with this result.
| ID | Name | email |
| 1 | Bill | bill2#mail.com |
I'd like to create a trigger that would have this in Users_History where 2 is the record merged from.
| Users_ID | Name | Email | Status |
| 1 | Bill | bill2#mail.com | MERGED:2 |
As it stands now, we would end up with two records, an UPDATE and a DELETE
UPDATE `Users_To`
SET `Name` = `Users_From`.`Name`, `email` = `Users_From`.`email`
FROM `Users` AS `Users_To`
CROSS JOIN `Users` AS `Users_From`
WHERE `Users_To`.`ID` = 1 AND `Users_From`.`ID` = 2;
DELETE FROM `Users` WHERE `ID` = 2;
I can't think of a way to do this in SQL, is it possible? As I understand, the triggers have no knowledge of the JOIN or WHERE clause affecting a record.
Your UPDATE query has a flaw and is correctly
UPDATE `Users` AS `Users_To` CROSS JOIN `Users` AS `Users_From`
SET `Users_To`.`Name` = `Users_From`.`Name`, `Users_To`.`email` = `Users_From`.`email`
WHERE `Users_To`.`ID` = 1 AND `Users_From`.`ID` = 2;
DELETE FROM `Users` WHERE `ID` = 2;
What the trigger does is simple first it check if the update is a modify of an existing row by checking if there are mor than one rows with the same email.
If that the case grab the other id in your case 2 and add it ti the text.
This works as long you have only 2 rows involved in your update query.
And i don't know your your table, but if you have a UNIQUE constraint you will have a problem with the update.
DELIMITER $$
DROP TRIGGER IF EXISTS Users_History_Update;
CREATE TRIGGER Users_History_Update AFTER UPDATE ON users
FOR EACH ROW BEGIN
SELECT Count(*) INTO #number FROM Users WHERE `email` = NEW.`email`;
IF #number > 1 THEN
SELECT `ID` INTO #id FROM Users WHERE `email` = NEW.`email` AND `ID` <> NEW.`ID`;
INSERT INTO Users_History VALUES (OLD.`ID`,OLD.`Name` ,OLD.`email`, CONCAT('MERGED:',#id));
ELSE
INSERT INTO Users_History VALUES (OLD.`ID`,OLD.`Name` ,OLD.`email`, 'UPDATE');
END IF;
END$$
DELIMITER ;
So I have two tables which look like this:
products_tbl (product_id, product_manufacturer, product_name)
manufacturers_tbl (manufacturer_id, manufacturer_name)
Now in case a product_manufacturer is inserted into products_tbl which does NOT yet exist in manufacturers_tbl, I would like to automatically insert the respective key into manufacturers_tbl.
I already found quite a lot of old posts on the general topic of "cascading" inserts, but none of them wanted to integrate such logic.
Is it possible with mysql only, or would I have to write some more backend/php logic to accomplish this?
EDIT:
If the above is not possible, I would like to know how to add a FOREIGN KEY CONSTRAINT which throws an error if a product_manufacturer in products_tbl is added which has no equivalent manufacturer_name in manufacturers_tbl.
Easy enough with a trigger but also easy to acquire junk.
drop table if exists products_tbl,manufacturers_tbl;
create table products_tbl (product_id int, product_manufacturer varchar(10), product_name varchar(10));
create table manufacturers_tbl (manufacturer_id int, manufacturer_name varchar(10));
drop trigger if exists t;
delimiter $$
create trigger t after insert on products_tbl
for each row
begin
if not exists(select 1 from manufacturers_tbl where manufacturer_name = new.product_manufacturer) then
insert into manufacturers_tbl(manufacturer_name) values (new.product_manufacturer);
end if;
end $$
delimiter ;
insert into products_tbl values
(1,'microsoft','a'),
(2,'msoft','b'),
(3,'mcrosoft','c'),
(4,'microsoft','d');
select * from manufacturers_tbl;
+-----------------+-------------------+
| manufacturer_id | manufacturer_name |
+-----------------+-------------------+
| NULL | microsoft |
| NULL | msoft |
| NULL | mcrosoft |
+-----------------+-------------------+
3 rows in set (0.00 sec)
Say I've got the following fields that make up a unique key in my table: (productid varchar(30), dateset date, dateend date)
I intend for dateend to be null until another row with the same productid with a newer dateset has been inserted, like this:
+----------+-------------+-------------+
|productid | dateset | dateend |
+----------+-------------+-------------+
|'PRODUCTA'|'2014-10-09' | NULL |
+----------+-------------+-------------+
|'PRODUCTA'|'2009-08-06' |'2014-10-09' |
+----------+-------------+-------------+
Now, I'm thinking about how I'd safely do this and I figure I'd run a trigger before the new row is inserted. It would essentially find the current max(dateset) where productid = #productid, and if it exists, it will update this row's dateend with #newdateset, the date that our new, succeeding row is inserted. The problem is, I have no idea how to get the variable #productid, or #newdateset. Is there any way to get the information of a row that's now about to be inserted? If I wrote:
insert into products (productid, dateset) values ('DEMOA', '2014-10-10')
How would I get my trigger to say #productid = 'DEMOA', #newdateset = '2014-10-10'?
Thanks,
Zakiir
To reference a column in a row that's now about to be updated, you'd write NEW.col_name, in my case:
NEW.productid
would reference 'DEMOA' in the following insert statement:
insert into products (productid, dateset) values ('DEMOA', '2014-10-10');
Like so:
delimiter //
CREATE TRIGGER update_row_version BEFORE INSERT ON products
FOR EACH ROW
BEGIN
SELECT productid, max(dateset) INTO #productid, #maxdateset FROM products WHERE productid = NEW.productid;
END;//
delimiter ;
INSERT INTO products (productid, price_local) VALUES ('DEMOA', 3000.00);
select #productid;
+-----------+
| #productid|
+-----------+
| DEMOA |
+-----------+
Apparently, what I was trying to achieve isn't possible as a trigger does not allow me to UPDATE the same table that I'm trying to INSERT into (Error 1442). There's already a post about it:
mysql trigger stored trigger is already used by statement which invoked stored trigger
I have this relation (A):
ID | B_ID
This relation (B):
ID | FOO
I want to add a trigger to A which will, on insertion of a new row into A (with B_ID always set as NULL), add a row in B with FOO set as NULL and set the B_ID attribute in the new row of A to reference the newly added B row.
Example:
Right after insertion into
A:
1 | NULL
After trigger action:
A:
1 | 555
B:
555 | NULL
Is this possible?
You can try something like the following:
/* Trigger structure for table `a` */
DELIMITER $$
CREATE TRIGGER `trg_a_bi` BEFORE INSERT ON `a`
FOR EACH ROW
BEGIN
INSERT INTO `b` (`foo`) VALUES (NULL);
SET NEW.`b_id` := LAST_INSERT_ID();
END$$
DELIMITER ;
Here a SQL Fiddle.