Getting information from a third table in a MySQL trigger - mysql

What I want to do is similar to this, except the action is an insert not an update. I will use the same example used in that question, modified for my context;
I want to do something like this:
CREATE TRIGGER au_TableA AFTER UPDATE TableA
FOR EACH ROW BEGIN
INSERT INTO TableB (cID, points) VALUES (NEW.cID, TableC.points);
END
cID is present in TableA and TableC. Because it's not an UPDATE I can't do a JOIN like in the other question (right?). Is there any way to get the data from TableC INSERTED into TableB here?

If I understand your question correctly, this should be the trigger that you need:
CREATE TRIGGER au_TableA AFTER UPDATE TableA
FOR EACH ROW
BEGIN
INSERT INTO TableB (cID, points)
SELECT TableC.cID, TableC.points
FROM TableC
WHERE TableC.cID = NEW.cID;
END
You don't need a JOIN in this case, but you could use the syntax INSERT INTO ... SELECT

Related

MySQL if condition inside a trigger

I have 2 tables, lets name it table A and table B, every time something is inserted in the table A I want some of the data (id, name, comment) automatically inserts also in the table B but only if there is no raw in table B with the same name.
Example:
If I insert OBJECT1 in A with values:
id=1, colour=2, name=example, price=5€, comment="blablabla"
it should be inserted in the table B a row with this values:
id=1, name=example, comment="blablabla"
but if then I insert OBJECT2 in A with values:
id=2, colour=5, name=example, price=10€, comment="aaa";
nothing should happen in the table B, because there is already a row in table B with name=example.
This is my code:
CREATE TRIGGER mytrigger
BEFORE INSERT ON tablea
IF NOT EXISTS (SELECT * FROM tableb WHERE tableb.name = NEW.name)
THEN
INSERT INTO tableb (id, name, comment)
VALUES (NEW.id, NEW.name, NEW.comment)
END IF
And this is the error I receive:
You have an error in your SQL Syntax...
I also tried adding BEGIN and END at the starting and end of the definition, also tried with END instead of END IF, also tried BEGIN instead of THEN
Does someone know what am I doing wrong?
In PL/SQL, you can't put an EXISTS() in an IF statement.
So please try this:
CREATE OR REPLACE TRIGGER mytrigger
BEFORE INSERT ON tablea REFERENCING NEW AS NEW
FOR EACH ROW
declare cnt integer;
BEGIN
SELECT count(*) into cnt FROM tableb WHERE tableb.name = :NEW.name;
IF cnt >0
THEN
INSERT INTO tableb (id, name, comment)
VALUES (:NEW.id, :NEW.name, :NEW.comment)
END IF
END;

MySQL : Can I use one SELECT ... FOR UPDATE to "protect" multiple tables? ( LOCKING )

I'm reading the MySQL docs for hours but I still cannot answer to myself a couple of pretty simple questions... :(
Here is my (simplified) scenario: I have two tables in a database: tablea and tableb, both tables use the InnoDB storage engine. tablea (which is my main table) has a PRIMARY index (id) with autoincrement. Now here is what I want to achieve and please keep in mind that the following business logic can be and will be run concurrently:
I start a transaction:
START TRANSACTION
BEGIN
then I check if an id exists in tablea if yes, I SELECT the row FOR UPDATE, let's call the id I am looking for myid :
SELECT `id` FROM `tablea` WHERE `id`='myid' FOR UPDATE;
if the above SELECT returns no rows, I simply ROLLBACK the transaction and exit from my function. In other words I'm done when myid is not present in tablea.
On the other hand when myid exists then first I need to update some values in tablea:
UPDATE `tablea` SET `somefield`='somevalue' WHERE `id`='myid';
then I need to check if myid also exists in tableb:
SELECT * FROM `tableb` WHERE `id`='myid' FOR UPDATE;
my first question is about the above SELECT statement: Is it okay to do another SELECT FOR UPDATE here (on tableb) ??? Or "FOR UPDATE" is not needed here when dealing with tableb, because I already started a transaction and also acquired a lock based on a row in tablea ??? Can someone please answer this?
The last SELECT statement above either returns a row from tableb (and locks that row for update) or it turns out that myid does not exist in tableb.
When myid is present in tableb then I just need to update some values in that row, it's simple:
UPDATE `tableb` SET `somefieldintableb`='somevaluefortableb' WHERE `id`='myid';
On the other hand when myid is not in tableb I need to insert it, and here comes my 2nd question: Should I lock tableb before I issue my INSERT INTO statement, like this:
LOCK TABLES `tableb` WRITE;
INSERT INTO `tableb` (`id`,`somefieldintableb`) VALUES ('myid','somevaluefortableb');
UNLOCK TABLES `tableb`;
and then finally, I do:
COMMIT
My goal is this: Since the above described function (with the MySQL transaction) will run in many instances in parallel, I want to prevent any of those instances updating the same row in either tablea or tableb at the same time. I also want to prevent double-insertion of myid into tableb, hence I thought about using LOCK TABLES when myid was not found in tableb.
So I have two questions: Should I do a SELECT ... FOR UPDATE within my already started transaction when I want to update tableb or locking tableb with SELECT ... FOR UPDATE is unnecessary, because holding the lock on tablea already "protects" tableb too from simultaneous UPDATEs in this case ??? Thanks to the way I started my transaction, I mean.
2nd question: When I need to INSERT a new row into tableb should I lock the whole table for that insertion? Or is that something that is totally unnecessary in this case? (Do I need LOCK TABLES tableb or not?)
I would appreciate if an expert can answer these two questions for me, because reading the various docs and examples online simply won't help me answering these questions. :(
I would do it this way:
BEGIN;
SELECT a.`id` AS a_id, b.`id` AS b_id
FROM `tablea` AS a LEFT OUTER JOIN `tableb` AS b ON a.id=b.id
WHERE a`id`='myid'
FOR UPDATE;
Now you have row locks on both tablea and tableb if rows exist. If the SELECT returns nothing, you know the id is not present in tablea. If the SELECT returns a row with a value for a_id, but a NULL for b_id, then you know it's present in tablea and not in tableb.
If the row is present in both tables, this locks rows in both tables simultaneously. If you do it in two steps, you might risk a race condition and deadlock.
Try the INSERT and use ON DUPLICATE KEY UPDATE:
INSERT INTO `tableb` (id, somefieldintableb) VALUES ('myid', 'somevaluefortableb')
ON DUPLICATE KEY UPDATE `somefieldintableb`='somevaluefortableb';
If the row with your desired id value is not present, this will insert it. If the row is present, this will update the row. And you're sure to have access to an existing row, because your SELECT FOR UPDATE locked it earlier.
Don't use table locks if you can avoid it. That's a sure way to create a bottleneck in your application.
Re your comments:
Yes, you can use extra join conditions for the date column.
You don't have to update all the columns when you use ON DUPLICATE KEY UPDATE. You can leave most of them alone if the row exists, and just update one, or a few, or whatever.
Also you can reference the value you tried to insert.
INSERT INTO `tableb` (id, date, col1, col2, col3, col4, col5, col6)
VALUES ('myid', $a_date, ?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE col4=VALUES(col4);
For more details, I recommend reading http://dev.mysql.com/doc/refman/5.7/en/insert-on-duplicate.html

Insert a record and also update existing records

hi there i was trying to right a Proc for a DataLoad when you want to insert records on a Table1 based on Table2
This is what i came of with
it has 2 condition
1) if the record does not exist create a new row of record
2) if the record already exist update the record based on keys
this is my proc need some help thanks
DECLARE #TableKey INT --(it is passed by the user proc param),
DECLARE #TableCount INT,
DECLARE #CLassKey INT,
SELECT #TableCount= COUNT(*) FROM Table1 WHERE Tablekey= #TableKey
INSERT INTO #CLassKey
SELECT Distinct c.PK_ClassKey FROM CLASS as c
INNER JOIN BOOK as B ON B.FK_ClassKey=C.PK_ClassKEy
IF ((SELECT COUNT(*) FROM #ClassKey) > 0 AND #TableCount= 0)--- this will check
BEGIN
Insert into NOTE
n.note
Select
c.note
FROM Class where c.FK_Note = n.PK_Note.
END
---- this will just insert for the first time..
How do i update it any idea as the records are only inserted for the first time put does not update using the same format thanks a lot
try this one
INSERT INTO table_name (id,col2,col3)
VALUES (value_id,value2,value3)
ON DUPLICATE KEY UPDATE
col2=value2,
col3=value3;

BEFORE INSERT TRIGGER on one table but insert should be done on another table

I have two tables say table1 and table2.
fields of table1 - id and count
fields of table2 - id and count
both tables have same fields. Now I want to create BEFORE INSERT TRIGGER on table1 which will check if count = 2 then insert into table2 otherwise in table1.
I created the trigger but when I fire this query ..example-
insert into table1 (id, count) values (1323, 2);
then one row is inserted into table2 (this I want) and same row is inserted into table1 also.
I want row should be inserted in table1 or table not in both tables.
what I do to achieve this ?
Try with this:
delimiter $$
CREATE TRIGGER myTrigger
BEFORE INSERT ON table1
FOR EACH ROW
BEGIN
IF NEW.count = 2 THEN
insert into table2 (id, count) values (New.id,New.Count);
ELSE
<<no action>>
END IF;
END$$
I'm not sure why you want this, so I suspect I might be giving advice on some sort of solution that should be fixed differently, but here we go.
As you can see in this question here it is not possible to neatly stop an insert. You could try and make an error so you'll not actually insert, but I think that is really painful.
What you can do is make a fake table dummytable and put your insert trigger there. Your row will always insert in the dummy table, but you either insert it in table1 or table2 according to your wishes. The meat of the trigger would look like
IF NEW.count = 2 THEN
INSERT INTO table2 (id, count) VALUES (New.id,New.Count);
ELSE
INSERT INTO table2 (id, count) VALUES (New.id,New.Count);
END IF;
You'll have to clean the dummytable now and then.

using a join inside a trigger, MySQL

I'm developing a database that needs to update a field in one table when another table is updated. However, the table that is being updated doesn't contain the values I need... it just contains an ID to a third table that has that value. Basically, I'm looking for something that does this:
CREATE TRIGGER au_TableA AFTER UPDATE TableA
FOR EACH ROW BEGIN
UPDATE TableB SET points=TableC.points
WHERE TableC.cID=NEW.cID;
END
cID, of course, being present in both TableA and TableC.
Try this query -
UPDATE
TableB b
JOIN TableC с
ON c.cID = b.cID
SET
b.points = c.points
WHERE c.cID=NEW.cID;