Can't Setup MySQL Trigger To INSERT Into Second Table - mysql

I have two tables which I use to store call details in. One table (Call_Detail) stores the header details against each call that gets entered, the second (Call_History) stores every comment against the call. So a single call will only appear ONCE in the Call_Detail table, but may appear multiple times in the Call_History table.
I currently run a Query to return the latest comment against a group of calls. So, I return the header details out of Call_Detail and then cross reference against the Call_History to find the 'newest' comment (thanks to some outside help). However, this Query can be quite time consuming when running against a large number of calls.
Therefore, I'm thinking to optimize my Query, I want to setup a trigger that records these details.
I am wanting to catch any INSERT command into the Call_History table and record the comment and date/time into the Call_Detail table against the relevant call ID.
So far I have the following but it doesn't like my syntax for some reason:
DELIMITER $$
CREATE TRIGGER Last_Call_Update
AFTER INSERT ON call_history
FOR EACH ROW
BEGIN
UPDATE call_detail
SET last_updated = NEW.updated_at, last_commment = NEW.body
WHERE id = NEW.ticket_id
END $$
DELIMITER ;

Add semicolon after UPDATE statement
DELIMITER $$
CREATE TRIGGER Last_Call_Update
AFTER INSERT ON call_history
FOR EACH ROW
BEGIN
UPDATE call_detail
SET last_updated = NEW.updated_at, last_commment = NEW.body
WHERE id = NEW.ticket_id;
END $$
DELIMITER ;

Related

How to UPDATE from within an INSERT trigger in MySQL? What other approach can I take?

I've the following trigger which is really bugging me.
I want to do an UPDATE inside the trigger, but MySQL doesn't allow it, since the statement "triggering" the trigger refer to the participants table.
What can I do in order to make it work? I've considered stored procedures, but they are also forbidden in this case.
The logic is: I've a table with participants in a meeting. The invariant that people from the 'tap' group should always accept the invitation if someone from the 'vip' group is attending. In this case, if a 'vip' is attending the meeting I must UPDATE all 'tap' attendances, but how is this possible???
DELIMITER $$
CREATE TRIGGER tap_meet_vip
BEFORE INSERT ON participants
FOR EACH ROW BEGIN
IF (NEW.pid IN (SELECT userid FROM people WHERE `group` = 'vip')) THEN # new participant is a member of the vip group
UPDATE participants SET `status` = 'a' WHERE pid IN (SELECT userid FROM people WHERE `group` = 'tap') AND meetid = NEW.meetid ; # update status to `accept` of all tap group participants
ELSEIF (NEW.pid IN (SELECT userid FROM people WHERE `group` = 'tap') AND EXISTS (SELECT * FROM participants INNER JOIN people ON pid = userid WHERE `group` = 'vip')) THEN # new participant is a member of the tap group and meeting has vip participant
SET NEW.`status` = 'a';
END IF;
END;$$
The above is written using MySQL and the version is 5.6.
It seems that you can't do all this in a trigger. Trigger is already locked the table so update statement can't get the lock. According to the mysql:
Within a stored function or trigger, it is not permitted to modify a
table that is already being used (for reading or writing) by the
statement that invoked the function or trigger
So you can create a stored procedure, that inserts into/Updates the target table, then updates the other row(s), all in a transaction. With a stored proc you'll manually commit the changes (insert and update).
DROP PROCEDURE IF EXIST marks;
DELIMITER $$
CREATE PROCEDURE marks(IN marks INT)
BEGIN
INSERT INTO first VALUES(marks);
END $$
DELIMITER ;
START TRANSACTION;
INSERT INTO first VALUES (16);
CALL marks(18);
COMMIT;

How to structure this IF condition in MySQL trigger

I am trying to write a MySQL trigger. I have two tables that look like this:
When a customer makes a purchase a new record is added to each table. I have added column ‘sku_copy’ to Table B, so it does not get populated when a new record is created.
When a new record is created, I want my trigger to copy the ‘sku’ field in Table A to the ‘sku_copy’ field in Table B. However, the problem I am having is how to structure the following condition in the trigger.
IF: ‘order_id’ in Table A matches ‘order_id’ in Table B. THEN: copy ‘sku’ from that Table A record to the record in Table B with the matching ‘order_id’. The data should be added to Table B ‘sku_copy'.
ELSE: don’t do anything.
Can someone show me how to write this into my trigger?
Thanks for any help you can give.
Try this but why do u think of using trigger for this ?
DELIMITER $$
CREATE TRIGGER trigger_name
AFTER INSERT ON tableA
FOR EACH ROW BEGIN
INSERT INTO tableB
SET sku_copy = OLD.sku,
order_id = OLD.order_id,
order = OLD.order;
END$$
DELIMITER ;
I want to post the trigger that was constructed with examples offered here and on another forum. Thanks to everyone who helped with this.
DELIMITER $$
CREATE TRIGGER sku_AfterInsert
AFTER INSERT ON uau3h_virtuemart_order_items
FOR EACH ROW BEGIN
UPDATE uau3h_virtuemart_orders
SET order_item_sku_copy = NEW.order_item_sku
WHERE virtuemart_order_id = NEW.virtuemart_order_id;
END$$;
DELIMITER ;

MySQL trigger to delete old record and insert new one

i have a table as below
Table name: sda_user_eform_data
ack_no Name Description
1 name1 This is name1
2 name2 This is name2
3 name3 This is name3
i have another table sda_user_eform_data_bckup which has exactly the same structure as sda_user_eform_data. I want to store only 5 rows(latest rows) in the sda_user_eform_data and whenever the ackno is greater than 5 the old values should be moved to the second(sda_user_eform_data_bckup) table.
For this first i have copied all the rows from sda_user_eform_data table to the sda_user_eform_data_bckup table. then i have created the following trigger where the i have checked the ack_no and if its greater than 5 then its deleted the oldest ack_no and insert the new value to the bckup table.
DELIMITER $$
create
trigger 'copy_eform_data' AFTER INSERT
on asdb.sda_user_eform_data
for each row begin
if (select count(s.ack_no) from asdb.sda_user_eform_data s)>5 then
delete from asdb.sda_user_eform_data where old.ack_no=(select min(s.ack_no) from asdb.sda_user_eform_data s);
insert into asdb.sda_user_eform_data_bckup select * from asdb.sda_user_eform_data where ack_no=select max(s.ack_no) from asdb.sda_user_eform_data s;
end$$
DELIMITER ;
I am not able to find out where the trigger went wrong as its not executing. Any suggestion is highly welcoming.
Thanks in advance.
That's most likely because your trigger doesn't even exist. The problem is here
create
trigger 'copy_eform_data'
With the single quotes copy_eform_data is a string.
Have a look at this post: When to use single quotes, double quotes, and backticks?
Also you should read up about the NEW and OLD keywords in triggers. Your trigger probably never matches a line.
And here
where ack_no=select max(s.ack_no) from asdb.sda_user_eform_data s
you're missing parantheses.
Apart from all that, I didn't really have a deep thought about your logic to be honest, because I don't see a point in your whole question. Why would you want to have duplicate data? I guess out of performance reasons? Have your table indexed appropriately and there should be no problem. And to get the 5 latest entries of your table simply use
FROM yourTable
ORDER BY when_was_the_entry_created_or_something DESC
LIMIT 5
You can have columns like
created timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
to use in your ORDER BY. And you probably want an index on that column.
I doing some changes in your query. Revert back if it helps.
DELIMITER $$
create
trigger 'copy_eform_data' AFTER INSERT
on asdb.sda_user_eform_data
for each row begin
insert into asdb.sda_user_eform_data_bckup
select * from asdb.sda_user_eform_data ORDER BY ack_no DESC LIMIT 1
if (select count(s.ack_no) from asdb.sda_user_eform_data s)>=5 then
delete from asdb.sda_user_eform_data;
end$$
DELIMITER ;
But make sure that when you are entering records into "asdb.sda_user_eform_data" after clearing table, "ack_no" should again start from 1
I was searching for a solution to create trigger for deleting records and came across your forum. However, I know this is over a year post and you've most likely solved the issue, but I would like to try to answer your post. I think you need a "End If" before your end $$.
So it would be:
DELIMITER $$
create
trigger 'copy_eform_data' AFTER INSERT
on asdb.sda_user_eform_data
for each row begin
if (select count(s.ack_no) from asdb.sda_user_eform_data s)>5 then
delete from asdb.sda_user_eform_data where old.ack_no=(select min(s.ack_no) from asdb.sda_user_eform_data s);
insert into asdb.sda_user_eform_data_bckup select * from asdb.sda_user_eform_data where ack_no=select max(s.ack_no) from asdb.sda_user_eform_data s;
END IF;
end$$
DELIMITER ;

MySQL write a trigger to prevent from buying too much

I have a table warehouse where I have information about articles in my store (article id as foreign key and quantity). Then, I have another table, shoppinglist where I have a clients id, article id and quantity. Lets say, that client wants to buy 3 articles but theres only one article available. How to write a trigger which help me to prevent from buying too much?
I tried this:
DELIMITER $$ CREATE TRIGGER check BEFORE INSERT ON shoppinglist FOR EACH ROW BEGIN IF warehouse.quantity < shoppinglist.quantity THEN CALL fail('You cant buy that much'); END IF; END $$ DELIMITER;
but this seems not to work. I mean, when I do:
INSERT INTO shoppinlist (clients_id, article_id, quantity) VALUES (1, 2, 100);
having only 2 articles with id = 2 on warehouse its ok, its possible. What did I do wrong?
What specific article would warehouse.quantity or shoppingList.quantity refer to in your code?
Also, check is a reserved keyword.
Try this:
DELIMITER $$
CREATE TRIGGER qtyCheck BEFORE INSERT ON shoppinglist
FOR EACH ROW
BEGIN
SET #qty = (SELECT quantity FROM warehouse WHERE article_id = NEW.article_id);
IF #qty < NEW.quantity THEN
CALL fail('You cant buy that much');
END IF;
END $$
DELIMITER ;
Note that I renamed the trigger, I'm guessing the name of the article_id column on the warehouse table, I used the NEW variable instead of shoppingList within the body of the trigger, and you need a space before the semicolon in DELIMITER ;, though this might've been a typo when posting.
Finally, you may get the following error if the fail function isn't defined. It doesn't exist on my system...
ERROR 1305: PROCEDURE testing.fail does not exist

mySQL trigger cannot update table : already in use by the statement that invoked the trigger

so i've edited my code
i had this problem in another trigger but this time even changing WHERE clause doesn't help
DELIMITER $$
CREATE TRIGGER pic_album_change AFTER UPDATE ON pictures
FOR EACH ROW BEGIN
UPDATE albums SET counter = counter + 1 WHERE albums.id = NEW.album_id;
UPDATE albums SET counter = counter - 1 WHERE albums.id = OLD.album_id;
END $$
DELIMITER ;
error :
<p>Error Number: 1442</p><p>Can't update table 'pictures' in stored function/trigger because it is already used by statement which invoked this stored function/trigger.
i dont see any changes on the pictures table in this trigger
i have another trigger that involves these two tables
DELIMITER $$
CREATE TRIGGER album_change
AFTER UPDATE
ON albums
FOR EACH ROW
BEGIN
UPDATE pictures
SET
level = NEW.level
WHERE
pictures.album_id = NEW.id ;
END $$
DELIMITER ;
Your WHERE clause is the wrong way round.
WHERE albums.id = NEW.album_id
would cause albums.counter to be incremented (presumably that's the number of pictures in each album).
It matters because joins on tables are not commutative — the direction of the join is important. Here you need to find the record in albums to update based on the value of the row in pictures.
While in this case there isn't really any ambiguity, SQL needs to follow rules about joins.
The issue with your second trigger is that MySQL is preventing a continuous cycle of updates. You have a trigger pic_album_change which updates albums when pictures is updated. And you have a trigger album_change which updates pictures when albums is updated. Those triggers will trigger each other.
It seems to me that the database design may need to be changed. Do you really need albums.counter when that data can be found with SELECT count(*) FROM pictures WHERE album_id=...? It should be possible to normalise data so that there no circular references in triggers. In fact triggers may not be necessary at all.