MYSQL Trigger to insert column value in same table - mysql

I have a mysql Innodb table 'classrooms_subjects' as
id|classroom_id|subject_id
classroom_id & subject_id are composite keys. Whenever i insert a row with classroom_id & subject_id, my id field is inserted as 0.
Now i want to create a trigger which will enter id field as last_inserted_id()+1.
Also I need to take care of multiple records inserted at a time. My trigger is like below:
CREATE TRIGGER `increment_id` AFTER INSERT ON `classrooms_subjects`
FOR EACH ROW BEGIN
UPDATE classrooms_subjects
SET classrooms_subjects.id = LAST_INSERT_ID() + 1 WHERE id=0;
END
when i am inserting a record I am getting the error as:
"Cant update table in trigger because it is already used by statement which invoked this trigger

For general info: using an update statement inside the trigger isn't right.
Better to use a before insert trigger and simply assign the value of your column using NEW.id
http://dev.mysql.com/doc/refman/5.7/en/trigger-syntax.html
A column named with OLD is read only. You can refer to it (if you have
the SELECT privilege), but not modify it. You can refer to a column
named with NEW if you have the SELECT privilege for it. In a BEFORE
trigger, you can also change its value with SET NEW.col_name = value
if you have the UPDATE privilege for it. This means you can use a
trigger to modify the values to be inserted into a new row or used to
update a row. (Such a SET statement has no effect in an AFTER
trigger because the row change will have already occurred.)
You should probably structure your table to make the auto_increment work properly. Better a solution that works when multiple sessions are inserting to the DB at once.

Related

MySQL auto increment on insert and update

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.

Trigger Preventing Record Insertion

I was trying to create trigger which can update value of column user_count of table user_details using value of u_count of table user_info.
CREATE TRIGGER `test`
AFTER INSERT ON `user_details` FOR EACH ROW
BEGIN
DECLARE default_user_count int(11);
SELECT u_count INTO #default_user_count FROM
user_info WHERE user_info.id= user_details.id_c;
IF user_details.user_count= 0
THEN UPDATE user_details SET
user_count = default_user_count
WHERE user_details.id_c = user_info.id;
END IF;
END
Trigger saved successfully but when i tried to insert value in both table it is preventing to insert record into user_details means no row inserted in 2 this table, if we delete trigger then its working.
Can anyone let me know wrong with this trigger?
THanks,
M.
It's not really clear what you're trying to accomplish, but it seems like it's something like what we have below.
There are numerous errors in and ambiguities in your trigger.
Confusion on variables -- DECLARE default_user_count INT(11); does not declare the user-defined variable #default_user_count. It declares the program variable default_user_count. The # prefix references an entirely different variable scope and namespace.
SELECT and UPDATE from the table which invoked the trigger doesn't usually make sense (SELECT) or is completely invalid (UPDATE).
With in a trigger, you are operating FOR EACH ROW -- that is, for each row included in the statement that invoked the trigger. Inside an INSERT trigger, the NEW values for the row are in a pseudo-table/pseudo-row accessible via the alias NEW. For UPDATE triggers, there are NEW and OLD row values, and for DELETE triggers, just OLD.
AFTER INSERT doesn't seem to make sense. I think you're looking for BEFORE INSERT -- that is, while processing an INSERT INTO ... query, before the newly-inserted row actually gets written into the table, modify its values accordingly. The resulting row contains the original values except where the trigger has modified them.
SELECT ... INTO a variable is a practice you should not get into the habit of, because it can bite you in a way a scalar subquery can't, by leaving a variable unexpectedly unaltered instead of setting it to NULL as would be expected. In this case, it would have made no difference, but it's still a caution worth mentioning... and in this case, I've eliminated that intermediate variable altogether, so the subquery is the only option.
If you are trying to set a value in this table using a value found in another table, all you need to do is SET NEW.column_name equal to the value you want used in the row instead of the value provided with the insert statement.
CREATE TRIGGER `test`
BEFORE INSERT ON `user_details` FOR EACH ROW
BEGIN
IF NEW.user_count = 0 /* maybe also >> */ OR NEW.user_count IS NULL /* << this */ THEN
SET NEW.user_count = (SELECT ui.u_count
FROM user_info ui
WHERE ui.id = NEW.id_c);
END IF;
END
Again, it's unclear how the two tables are connected based on the content of the original question, but this appears to do what you're trying to accomplish.

trigger to maintain total

I have created some tables for invoicing.
There is a invoice_header and invoice_detail table. The invoice details contains in the invoice detail lines, invoice_header_id, qty, unit_price, tax_amount etc
The invoice header has the billing address, invoice no, invoice_total, etc
I have created a trigger for insert/update/delete so that the invoice_total in the header is the sum(invoice_details.qty * invoice_details.unit_price)
There is one case I am not sure how to cater for.
If I update an invoice_detail line, to associate it with a different header.
like this
UPDATE invoice_details SET invoice_header_id=1 WHERE invoice_header_id=2
The trigger will fire but it will update the old header record total but not the new one. How can I cater for that circumstance?
Wouldn't this work for you?
From Mysql trigger syntax
Within the trigger body, the OLD and NEW keywords enable you to access
columns in the rows affected by a trigger. OLD and NEW are MySQL
extensions to triggers; they are not case sensitive.
In an INSERT trigger, only NEW.col_name can be used; there is no old
row. In a DELETE trigger, only OLD.col_name can be used; there is no
new row. In an UPDATE trigger, you can use OLD.col_name to refer to
the columns of a row before it is updated and NEW.col_name to refer to
the columns of the row after it is updated.
A column named with OLD is read only. You can refer to it (if you have
the SELECT privilege), but not modify it. You can refer to a column
named with NEW if you have the SELECT privilege for it. In a BEFORE
trigger, you can also change its value with SET NEW.col_name = value
if you have the UPDATE privilege for it. This means you can use a
trigger to modify the values to be inserted into a new row or used to
update a row. (Such a SET statement has no effect in an AFTER trigger
because the row change will have already occurred.)
So, you would have OLD.invoice_header_id (2) and NEW.invoice_header_id (1) Inside the trigger for you to update invoice_header
CREATE TRIGGER check BEFORE UPDATE ON invoice_detail
FOR EACH ROW
BEGIN
IF NEW.invoice_header_id <> OLD.invoice_header_id THEN
You Do the math and update both invoice_header lines
END IF;
END

MySQL Trigger cannot update table - getting ERROR 1442

I have the following trigger:
CREATE TRIGGER sum
AFTER INSERT
ON news
FOR EACH ROW
UPDATE news SET NEW.sum = (NEW.int_views + NEW.ext_views)/NEW.pageviews
It sums the int_views and ext_views column of a table and divides them by the total pageviews.
Whenever I try to add a new row to news, I get the following error:
ERROR 1442 (HY000) at line 3: Can't update table 'news' in stored function/trigger because it is already used by statement which invoked this stored function/trigger.
The trigger seems pretty simple to me. Is there a reason why the trigger fails to run?
The symptom is, that you are running an UPDATE (for all rows) inside a INSERT trigger - both modify the table, which is not allowed.
That said, if I guess the intention of your trigger correctly, you do not want to update all rows, but only the newly inserted row. You can achieve that easily with
CREATE TRIGGER sum
BEFORE INSERT
ON news
FOR EACH ROW
SET NEW.sum = (NEW.int_views + NEW.ext_views)/NEW.pageviews
Mind that this is a BEFORE INSERT trigger, as you want to change the row before it is written to the table.
If you try to update/insert on the same table that cause trigger to fire do not use the common sql command like
-> UPDATE TABLE_NAME SET COLUMN_NAME = VALUE WHERE CONDITION_LIST;
-> INSERT INTO TABLE_NAME VALUES("VALUE1","VALUE2");
This will not work. Only use set to assign the value of the column you update.
Example:
CREATE TRIGGER trigger_name BEFORE/AFTER INSERT/UPDATE ON table_name
FOR EACH ROW
SET NEW.COLUMN_NAME = "VALUE";
I was in a similar condition where I had to run two triggers:
UPDATE 3 fields on INSERTing a new ROW
UPDATE 3 fields on UPDATEing a ROW
After lot of efforts, I was finally able to write the TRIGGER in following way:
FOR updating values on INSERT
CREATE TRIGGER `INSERT_DISCOUNT_SERVICES` BEFORE INSERT ON `services`
FOR EACH ROW SET
NEW.discount_5_rate = (NEW.ndis_rate*0.05),
NEW.discount_10_rate=(NEW.ndis_rate*0.10),
NEW.discount_15_rate=(NEW.ndis_rate*0.15)
Similarly
FOR updating values on UPDATE
CREATE TRIGGER `UPDATE_DISCOUNTS_SERVICES` BEFORE UPDATE ON `services`
FOR EACH ROW SET
NEW.discount_5_rate = (NEW.ndis_rate*0.05),
NEW.discount_10_rate=(NEW.ndis_rate*0.10),
NEW.discount_15_rate=(NEW.ndis_rate*0.15)

Updata calculated field in "AFTER INSERT' trigger

I need some help writing a MySQL trigger.
what i want to do is to calculated hashe value of autoincrement ID and store it in separate column:
create trigger hashing after insert on categories
for each row begin
set new.hashed = md5(CONCAT("key",`categories `.`id`));
end;
but i get error:
[Err] 1362 - Updating of NEW row is not allowed in after trigger
Can someone help me achieve what i need? Is this possible at all using triggers?
Thanks
What you are trying to do is not supported.
You can only set the value of the "hashed" column in a BEFORE INSERT trigger, not an AFTER INSERT trigger.
However, you can't access the value of the auto-increment "id" column in a BEFORE INSERT trigger because that value is not known yet.
If you are determined to do this via a trigger, one option is to create a separate table to store the hashed value (along with a foreign key to reference the "categories" table), and insert into that table using an AFTER INSERT trigger on your "categories" table.