I am running this trigger
DELIMITER //
CREATE TRIGGER lestrigger
AFTER INSERT ON examinations
FOR EACH ROW
BEGIN
DECLARE the_last_inserted_id INT;
SELECT LAST_INSERT_ID() INTO the_last_inserted_id;
END //
DELIMITER ;
After insert,the last_inserted_id variable holds the last_insert_id of the previous insert instead of the current.
To fix this,i did SELECT LAST_INSERT_ID()+1 INTO the_last_inserted_id; but this is not really a fix since i don't know why the trigger is not working as it should.There is a similar question here but i don't understand it.Should i always add 1 to my trigger like the way i have done it?.
No, don't add 1 to last_insert_id() in the trigger.
In a multi-user, multi-threaded environment, you have no guarantee (and no expectation) that this will get you the id value that was assigned to the row that was just inserted. (An INSERT statement can insert more than one row, and a value for ID can be supplied so that it is not auto-generated.)
If what you want is the value that was actually assigned to the id column of the row that was just inserted (whether that was auto-generated, or whether the INSERT statement inserted more than one row), the do this:
SET the_last_inserted_id = NEW.id;
That gets the actual value that was assigned to the row (in an AFTER UPDATE FOR EACH ROW trigger. In a BEFORE UPDATE ... FOR EACH ROW trigger, this value could actually be modified.)
The behavior you are observing isn't wrong; it's what we expect. The behavior of the LAST_INSERT_ID() function is well documented here:
http://dev.mysql.com/doc/refman/5.5/en/information-functions.html#function_last-insert-id
Related
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.
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.
I have a MySQL trigger using the BEFORE INSERT ON table that calculates a value and updates the same table after a user inserts values in specific columns. This works as expected. But a user makes a mistake in their entry and fixes their error and I want to write a trigger that will update the calculated value after the error has been fixed. Is there a way to achieve this?
A BEFORE UPDATE ON table trigger has access to the existing values in the row as well as newly supplied values, and can set the value of any column in the table, based on whatever conditions and expressions we want.
For example, it's possible to test whether the value of one or more columns of concern has been modified, and then set some other column to some expression.
DELIMITER $$
CREATE TRIGGER my_before_update_trigger
BEFORE UPDATE ON my_table
FOR EACH ROW
BEGIN
IF NOT ((NEW.col1 <=> OLD.col1) AND (NEW.col2 <=> OLD.col2)) THEN
SET NEW.col3 = NEW.col1 * NEW.col2 ;
END IF;
END$$
DELIMITER ;
So this should be a fairly straight forward trigger, but my MySQL isn't great, so it's undoubtably a failure on my part.
It's not updating the stats table at all, even though it should be;
DROP TRIGGER countryUpdate;
DELIMITER //
CREATE TRIGGER countryUpdate AFTER INSERT ON stats
FOR EACH ROW BEGIN
DECLARE NewIP varchar(16);
DECLARE NewCountry varchar(80);
SET NewIP = inet_aton(new.vis_ip);
SET NewCountry = (SELECT country FROM iptocountry WHERE lower_bound <= NewIP AND upper_bound >= NewIP)
UPDATE stats
SET Country = NewCountry
END //
DELIMITER;
Well, first off, your UPDATE—if it works at all—is changing all rows in the stats table, and its doing that for each row inserted. That really doesn't make much sense. At minimum, you want to add a where clause to only hit the one row you've just inserted.
Apparently, though, that can't work at all in MySQL, because "a stored function or trigger cannot modify a table that is already being used (for reading or writing) by the statement that invoked the function or trigger." (Look under “Restrictions for Stored Functions”)
So, instead, you need to use a a before insert trigger, and do a SET new.country = NewCountry to fix the row up before its ever inserted.
I have a trigger that sets a datetime field in a table row when a new row is inserted. (Don't bother lecturing me that I could do this in the table definition, I have second datetime field that is using that functionality already and you can only do it with one column per table.)
This trigger works great for my purposes:
CREATE TRIGGER foobar_insert
BEFORE INSERT ON foobar
FOR EACH ROW SET NEW.created=NOW();
So whenever a new row is inserted, foobar.created gets set to the current time. This is great when I do something like:
INSERT INTO foobar (foo) VALUES ('bar');
The only problem with this is that if I want to explicitly set foobar.created in the insert statement, it gets overridden by the trigger.
So,
INSERT INTO foobar (foo,created) VALUES ('foo','2006-01-01 12:12:12');
results in foobar.created equaling the time of the insert, not the time specified in the insert statement.
So my question is: How can I change my trigger to only set foobar.created if it doesn't already have a value?
EDIT:
In response to james_bond below, here is what worked:
DELIMITER $$
DROP TRIGGER IF EXISTS foobar_insert$$
CREATE TRIGGER foobar_insert
BEFORE INSERT ON foobar
FOR EACH ROW
IF NEW.created IS NULL THEN
SET NEW.created=NOW();
END IF$$
DELIMITER ;
Note I had to include DELIMITER statements because the IF statement required an interior semicolon.
Test for NEW.created is NULL, if is set it's because you have set it in your insert statement,if it is NULL then it needs the current time as value, something like this will do the trick:
if NEW.created is NULL THEN
SET NEW.created = now();
end if