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.
Related
I have a MySQL database and I need to auto increment a column by 1 every time I do an insert or update. If I had to increment the column only during insert I could have used the built-in autoincrement option (usually used for primary keys). How can I do it for insert and updates?
EDIT
Sorry, I posted the wrong question, what I actually need is to increase a counter by 1 every time I do an insert or update, the current value of the counter has to be stored in the row being created or updated. The counter starts from 1 and never comes back, it just keep increasing "forever" (BIGINT). Think of this counter as a lastupdate timestamp but instead of using real unix timestamps I use an ever increasing integer (monotonic increasing value).
P.S. I'm implementing a syncronization mechanism between many local SQLite databases and one master MySQL database so the behavior has to be implemented on both dbms.
The current state of the counter can be stored on a separate table of course
Simply use triggers.
Something like this:
CREATE TRIGGER trgIU_triggertestTable_UpdateColumnCountWhenColumnB
ON dbo.triggertestTable
AFTER INSERT,UPDATE
AS
BEGIN ...
OR you can do something like this:
INSERT INTO TableA (firstName, lastName, logins) VALUES ('SomeName', 'SomeLastName', 1)
ON DUPLICATE KEY UPDATE count = count + 1;
I see two ways to do what you want.
The first is for inserts, when you should use the autoincrement key. But, when we talk about autoincrement updates, it's a little bit more complicated. For me, the best solution is to do a trigger.
You could use a trigger like this:
CREATE TRIGGER update_trigger
AFTER UPDATE
ON `your_table`
FOR EACH ROW
BEGIN
UPDATE `your_table`
SET `the_field_you_want_autoincrement` = `the_field_you_want_autoincrement` + 1
WHERE `pk` = NEW.pk
END
There's no declarative auto-increment-on-update feature. And the auto-increment must be part of your primary key, so this is probably not your counter.
You can do this with triggers.
CREATE TRIGGER MyTrigger BEFORE INSERT ON MyTable
FOR EACH ROW
SET NEW.counter = 1;
CREATE TRIGGER MyTrigger BEFORE UPDATE ON MyTable
FOR EACH ROW
SET NEW.counter = OLD.counter+1;
These must be BEFORE triggers, because you can't set column values in an AFTER trigger.
Re your comments:
I don't get the "for each row on the second statement"
This is a required clause for all MySQL triggers, because the trigger runs for each row inserted. You can insert multiple rows in a single INSERT statement:
INSERT INTO MyTable VALUES (...), (...), (...), ...
INSERT INTO MyTable SELECT ... FROM ...
The insert trigger will initialize each row inserted.
Re your updated question:
The solution with triggers I show above will actually work for the scenario you describe, where you want a counter column to start at 1 at INSERT time, and increase by 1 every time you update.
The solution with INSERT...ON DUPLICATE KEY UPDATE does not work, because it won't increment the counter if a user simply does an UPDATE statement. Also the user is required to include the initial counter value 1 in their INSERT statement.
The insert trigger sets the initial value to 1 even if a user tries to give a different value in their INSERT statement. And the update trigger will increment the counter even if the user uses INSERT...ON DUPLICATE KEY UPDATE or UPDATE.
But don't use a REPLACE statement, because this would do a DELETE followed by a new INSERT, and thus it would run the insert trigger, and reset the counter to 1.
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.)
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
I'm trying to create a trigger that will allow only one record in a database, so it would delete any previous records.
But currently, it doesn't allow me to insert anything it, because it's instantly deleted.
DELIMITER $$
CREATE TRIGGER test_insert
BEFORE INSERT ON test
FOR EACH ROW BEGIN
DELETE FROM test WHERE id = NEW.id - 1;
END$$
How would I delete a previously (or all previous) inserted record?
"But currently, it doesn't allow me to insert anything it, because it's instantly deleted."
Acutally, when you do an INSERT, the execution of your trigger should be throwing exception:
Error Code: 1442
Can't update table 'test' in stored function/trigger because it is
already used by statement which invoked this stored function/trigger.
(Unless something is radically different in a newer version of MySQL.)
The operation you want to perform (i.e. deleting rows from the same table you are inserting into) cannot be done in a MySQL trigger.
You could use a combination of a UNIQUE KEY and a BEFORE INSERT trigger to prevent more than one row from being inserted. The BEFORE INSERT trigger could set the value of the column that has a unique
key on it to be a static value, then the INSERT statement would throw a duplicate key ("Duplicate entry") exception.
Then, you could use an INSERT ... ON DUPLICATE KEY UPDATE ... statement to update the values of columns other than the unique id, e.g.
CREATE TRIGGER `test_insert` BEFORE INSERT ON test
FOR EACH ROW BEGIN
SET NEW.id := 1;
END
ALTER TABLE test ADD UNIQUE KEY (id);
INSERT INTO test (somecol) VALUES ('someval')
ON DUPLICATE KEY UPDATE somecol = VALUES(somecol) ;
To do that you first need to find some value at which you want to delete all and insert for. This way you don't need a trigger, you simply delete all previous cases and then add a new row. Unless for some reason unexplained from your code you would need a trigger a simple solution in your loop could work Like:
$query = mysql_query("DELETE FROM test WHERE id = NEW.id -1");
$new_id = $new.id -1;
$query2 = mysql_query("INSERT INTO test VALUES('$new_id','$var1','$var2'));
I want to save the mysql query used to delete a row in a table:
Example:
CREATE TRIGGER `table_DEL` BEFORE DELETE ON `table`
FOR EACH ROW BEGIN
INSERT INTO db_bk.table
SELECT *,NOW(),QUERY()
FROM db.table
WHERE table_id= OLD.table_id;
END
As you understand, I want to now if exists a query() function or another method to retrieve the query that activate the trigger (the exact delete query)
Thank you very much
As #devart said - such a function doesn't exist. If you are worried about who will delete from your table then restrict the 'delete' permission to one account. Then you can control how records are removed and when.