mysql trigger for loop - mysql

I'm trying to do some mysql trigger coding. I fail every time I meet loops.
CREATE TRIGGER `after_insert` AFTER INSERT ON `table_users`
FOR EACH ROW BEGIN
INSERT INTO table_user_plan (user_id, plan_id) VALUES
(NEW.id, (SELECT id FROM table_plans))
;
END
Here trigger is successfully created, but I get error
#1242 - Subquery returns more than 1 row
I understand this cannot work, because there are more than one row in table_plans... but how can I handle this if I want to add multiple rows or how can I make a loop and firstly select plans then insert into table_users?
Thanks in advanced

To insert multiple rows based on a SELECT you would use the INSERT ... SELECT FROM ... syntax. In this case, you would use something like
INSERT INTO table_user_plan
SELECT NEW.id AS user_id, tp.id AS plan_id
FROM table_plans tp;
(I think that should work, although I've never actually tried to use NEW in this context.)

Related

mySQL TRIGGER error code 1064

USE test;
CREATE TRIGGER AvgUpdateTrigger AFTER INSERT ON test.score
FOR EACH ROW
BEGIN
INSERT INTO test.average (test.average.TestID, test.average.TestAvg)
(SELECT test.score.TestID, avg(test.score.ScoreValue) FROM test.score GROUP BY test.score.TestID)
ON DUPLICATE KEY
UPDATE test.average.TestAvg = (SELECT avg(test.score.ScoreValue) FROM test.score WHERE test.score.TestID = test.average.TestID GROUP BY test.score.TestID);
END;
im trying to update one table(average) when another one gets changed(score)
it is telling me to add a semicolon but as you can see there is one there allready
If a trigger (or any stored procedure) contains only one statement, you don't need BEGIN and END:
CREATE TRIGGER AvgUpdateTrigger AFTER INSERT ON test.score
FOR EACH ROW
INSERT INTO test.average (TestID, TestAvg)
SELECT test.score.TestID, avg(test.score.ScoreValue)
FROM test.score
GROUP BY test.score.TestID
ON DUPLICATE KEY UPDATE
test.average.TestAvg = VALUES(TestAvg);
I also replaced the subquery with VALUES(TestAvg), since this value has already been selected.

What is faster: to call a user-declareed function within a query or to set the value by trigger?

There is a declared MySQL function GETUSERID() returning an integer value. How to make a record insert faster: setting the value from inside a query like
INSERT INTO ttable
(idtoset, some_other_field...)
VALUES (GETUSERID(), value1...);
or call
INSERT INTO ttable
(some_other_field...)
VALUES (value1...);
and fill idtoset by a trigger that fires before insert?
What if the query is performing multiple row insert like
INSERT INTO ttable
(idtoset, some_other_field...)
VALUES (GETUSERID(), value1...),
(GETUSERID(), value2...),
...
(GETUSERID(), valueN...);
?
Edit
I have just investigated the answer of #Rahul.
I created a ttest table with two triggers
CREATE TRIGGER `tgbi` BEFORE INSERT ON `ttest` FOR EACH ROW BEGIN
SET NEW.testint=1;
END;
CREATE TRIGGER `tgbi` BEFORE UPDATE ON `ttest` FOR EACH ROW BEGIN
SET NEW.testint=2;
END;
If I am not mistaken, should the before insert trigger call UPDATE SET the second trigger is expected to fire as well and the created testint value might be =2, but it is =1 in every inserted row. Could that mean that the engine optimises INSERT procedure and sets the value simultaneously with that set manually by query?
Appended on request of #Rick-James. The question is not about the definite function. It is actually about any function. Any function will be called same number of times if the record is inserted from trigger or from INSERT query. That is why I am wondering what is better from the point of MySQL engine - to call it manually setting the value in inserted records or filling it by means of triggers?
CREATE DEFINER=`***`#`***` FUNCTION `GETUSERID`() RETURNS int(10)
BEGIN
DECLARE id_no INT DEFAULT -1;
SELECT `id` INTO id_no FROM `tstuff`
WHERE `tstuff`.`user_name`=
(SELECT SUBSTRING_INDEX(USER(), '#', 1)) LIMIT 1;
RETURN id_no;
END
What is faster? No idea since I haven't done a bench marking on that but doing an direct INSERT operation would better to my knowledge instead of inserting and then perform an UPDATE through trigger.
Does what you are doing currently not working? you can as well make it a INSERT .. SELECT operation like
INSERT INTO ttable (idtoset, some_other_field...)
SELECT GETUSERID(), value1..., valuen FROM DUAL;
In past versions of MySQL, using a before insert trigger to populate a not nullable column didn't work as MySQL was evaluating the provided columns before the trigger. That's why whenever I have such a situation, I usually tend to go with functions instead of triggers.
From a performance point of view, since the before insert trigger is evaluated before actually writing data so the time needed to perform this is almost the same as immediately getting the value with the function and without trigger. But if all you are doing in the trigger is set the user ID, then I really see no reason to use a trigger.

MySQL Trigger with a Inner Join

I have two tables in mySQL, call them reviews and products. And then I have another table (call it productsmaster) that is an inner join of reviews and products. I am trying to create a trigger to run the inner join query when a new record is added to reviews.
I have tried to just insert the inner join query into the trigger, but it is returning a "#1422 - Explicit or implicit commit is not allowed in stored function or trigger." error. For clarity of the issue my code for the trigger is:
CREATE TRIGGER updateprodmaster
AFTER INSERT ON reviews
FOR EACH ROW
BEGIN
CREATE TABLE productsmaster AS
SELECT products.*, reviews.userid, reviews.usergender, reviews.userage, reviews.score
FROM products
INNER JOIN reviews
ON products.pid=reviews.pid;
END;$$
If anyone has any thoughts on this it would be much appreciated. Thanks!
Jack
The CREATE TABLE statement causes an implicit COMMIT. And that's not allowed.
There are no easy button workarounds to this limitation.
But even if you were able to workaround this limitation, why in the plastic would you want to attempt to create a new table every time a row is inserted?
When a second row is inserted, the trigger would to attempt to (again) create a table of the exact same name (which will fail because a table of that name already exists.)
Back the cart up a bit, behind the horse.
And figure out what requirement you need to meet.
When you get back to needing a trigger, you can burn that bridge when you get to it.
FOLLOWUP
If the intent is to attempt to insert a row into productsmaster table, whenever a row is inserted into the reviews table, using an after insert trigger, we'd need an INSERT statement in the trigger body.
The values of the columns of the row (that was just inserted to reviews) are available in the trigger. There's no need to select from the reviews table. We can reference the column values of the newly inserted row (in an after insert trigger) by qualifying the column names with NEW.
I recommend avoiding .*, and explicitly name the columns to be retrieved from products. I'm assuming the pid column is a unique key (or the primary key) in products.
As an example:
DELIMITER $$
CREATE TRIGGER trg_reviews_after_insert
AFTER INSERT ON reviews
FOR EACH ROW
BEGIN
INSERT INTO productsmaster (col1, col2, userid, usergender, userage, score)
SELECT p.col1
, p.col2
, NEW.userid
, NEW.usergender
, NEW.userage
, NEW.score
FROM products p
WHERE p.pid = NEW.pid;
END$$
DELIMITER $$
never use the select in triggers , triggers accept only update or insert or delete

SQL check if existing row, if not insert and return it

I'm having a problem with my sql query. I need to insert a data that needs to be checked first if it is existing or not. If the data is existing the sql query must return it, if not insert and return it. I already google it but the result is not quite suitable to my problem. I already read this.
Check if a row exists, otherwise insert
How to 'insert if not exists' in MySQL?
Here is a query that' I'm thinking.
INSERT INTO #tablename(#field, #conditional_field, #field, #conditional_field)
VALUES(
"value of field"
(SQL QUERY THAT CHECK IF THERE IS AN EXISTING DATA, IF NOT INSERT THE DATA and RETURN IT, IF YES return it),
"value of feild",
(SQL QUERY THAT CHECK IF THERE IS AN EXISTING DATA, IF NOT INSERT THE DATA and RETURN IT, IF YES return it)
);
Please take note that the conditional field is a required field so it can't be NULL.
Your tag set is quite weird, I'm unsure you require all the technologies listed but as long as Firebird is concerned there's UPDATE OR INSERT (link) construction.
The code could be like
UPDATE OR INSERT INTO aTable
VALUES (...)
MATCHING (ID, SomeColumn)
RETURNING ID, SomeColumn
Note that this will only work for PK match, no complex logic available. If that's not an option, you could use EXECUTE BLOCK which has all the power of stored procedures but is executed as usual query. And you'll get into concurrent update error if two clients execute updates at one time.
You could split it out into 2 steps
1. run a select statement to retrieve the rows that match your valus. select count (*) will give you the number of rows
2. If zero rows found, then run the insert to add the new values.
Alternatively, you could create a unique index form all your columns. If you try to insert a row where all the values exist, an error will be returned. You could then run a select statement to get the ID for this existing row. Otherwise, the insert will work.
You can check with if exists(select count(*) from #tablename) to see if there is data, but with insert into you need to insert data for all columns, so if there is only #field missing, you cant insert values with insert into, you will need to update the table and go with a little different method. And im not sure, why do you check every row? You know for every row what is missing? Are you comparing with some other table?
You can achieve it using MySQL stored procedure
Sample MySQL stored procedure
CREATE TABLE MyTable
(`ID` int, `ConditionField` varchar(10))
;
INSERT INTO MyTable
(`ID`, `ConditionField`)
VALUES
(1, 'Condition1'),
(1, 'Condition2')
;
CREATE PROCEDURE simpleproc (IN identifier INT,ConditionData varchar(10))
BEGIN
IF (SELECT ID FROM MyTable WHERE `ConditionField`=ConditionData) THEN
BEGIN
SELECT * FROM MyTable WHERE `ConditionField`=ConditionData;
END;
ELSE
BEGIN
INSERT INTO MyTable VALUES (identifier,ConditionData);
SELECT * FROM MyTable WHERE `ConditionField`=ConditionData;
END;
END IF;
END//
To Call stored procedure
CALL simpleproc(3,'Condition3');
DEMO

Mysql Trigger - Compound statements

Looking for some help on mysql triggers and using multiple statements.
What I am trying to achieve is BEFORE an insert I want to copy records from active table ->history table and then delete them, THEN do the insert. The code works perfectly for the copying of the records from active->history. But the Trigger bonks as soon as I put the delete in ie. it will still move the records but will NOT delete the old rows.
Thoughts/ideas?
CREATE
DEFINER = 'root'#'%'
TRIGGER tradesfinder.On_Checkin_active_Insert
BEFORE INSERT
ON tradesfinder.user_checkin_active
FOR EACH ROW
BEGIN
INSERT INTO user_checkin_history
SELECT *
FROM user_checkin_active
WHERE user_id = new.user_id;
DELETE FROM user_checkin_active
WHERE user_id = new.user_id;
END
Thanks in advance.
C
I think you need an update if the id exists not delete and then insert. Try this:
INSERT INTO `user_checkin_active` (user_id, b, c)
VALUES (?, ?, ?)
ON DUPLICATE KEY UPDATE b =new.b, c = new.c;
I think the trigger syntax as shown above will not work as expected. Instead of triggering a select *, it would be better to work with the new row. So instead of "insert into user_checkin_history select *..." you would instead want to have the trigger to have soemthing to the effect of "insert into user_checkin_history (a,b,c,d) values (new.a,new.b,new.c,new.d)" which then inserts ONLY the contents of the new row.
Otherwise, you end up picking up ALL rowsx for the given user EACH time the user inserts something. That may not be what you intended.