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.
Related
I'm fairly new to triggers and have already tried searching for a solution to my question with little results. I want to update a single row's start time column whenever it's active column is set to 1.
I have two columns ACTIVE (number) and START_TIME (timestamp) in my_table. I would like to create a PL/SQL trigger that updates the START_TIME column to current_timestamp whenever an update statement has been applied to the ACTIVE column - setting it to 1.
So far I have only seen examples for inserting new rows or updating entire tables which isn't what I'm looking to do. I'd have thought there would be a fairly simple solution to my problem.
This is what I've got so far from other examples. I know the structure of my solution is poor and I'm asking for any input to modify my trigger to achieve my desired result.
CREATE OR REPLACE TRIGGER routine_active
AFTER UPDATE ON my_table
FOR EACH ROW
WHEN (my_table.ACTIVE = 1)
begin
insert my_table.start_time = current_timestamp;
end;
\
you can use like this .it may help you
write the update query instead of insert query
CREATE OR REPLACE TRIGGER routine_active
AFTER UPDATE ON my_table
FOR EACH ROW
WHEN (new.ACTIVE = 1)
begin
update my_table set start_time =current_timestamp;
end;
I think it should be a BEFORE UPDATE, not AFTER UPDATE, so it saves both changes with a single action. Then you don't need the INSERT or UPDATE statements. I also added the "OF active" clause, so it will only start this trigger if that column was updated, which may reduce the workload if other columns get updated.
CREATE OR REPLACE TRIGGER routine_active
BEFORE UPDATE OF active ON my_table
FOR EACH ROW
BEGIN
IF active = 1
THEN
:NEW.start_time = current_timestamp;
END IF;
END;
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 need to update a table with pre-calculated values from tables where data can be added/updated/deleted.
I could use
insert into precalculated(...)
select ... from ...
on duplicate key update ...
to add/update the pre-calculated table but is there an optimized method to delete the obsolete rows ?
I think you should create a stored procedure that deletes the data of your related tables if and only if the records fulfill a condition.
There's not enough information in your question to design the procedure, but I can give you a little example:
delimiter $$
create procedure delete_orphans()
begin
declare id_orphan int;
declare done int default false;
declare cur_orphans cursor for
select distinct d.id
from data as d
left join precalculated as p on d.id = p.id
where p.id is null;
declare continue handler for not found set done = true;
open cur_orphans;
loop_delete_orphans: loop
fetch cur_orphans into id_orphan;
if done then
leave cur_orphans;
end if;
delete from data where id = id_orphan;
end loop;
close cur_orphans;
end$$
delimiter ;
This procedure will delete every row in the data table that does not have at least one related row in the precalculated table.
Of course, this approach might be inneficient, because it will delete the rows one by one, but as I said this is only an example. You can customize it to fit your needs.
You can call this procedure from a trigger if you want (with call delete_orphans()).
Hope this helps.
Since you are always adding or updating rows that exist in these other tables, and you want to remove any rows that don't exist, why don't you just :
DELETE FROM precalculated
insert into precalculated(...)
select ... from ...
on duplicate key update ...
Always starting clean means you don't have to worry about orphans later.
You could add triggers for insert, delete and update on the main tables that maintains precalculated.
When inserting or updating the same code can be used to calculate the values and issuing a replace into precalculated (...) values (...)
When deleting it's probably the same, with the addition that you'll also delete rows from precalculated that are orphans. Be smart here and use values from the original delete to query precalculated for orphans instead of doing a table scan.
I may have found my solution using rename.
so basically, I will do a simple insert select to the temporary table and then
rename precalculated to precalculated_temprename, precalculated_temp to precalculated, precalculated_temprename to precalculated_temp;
truncate precalculated_temp;
need some tests but it seems the rename operation is fast and atomic.
I have been trying to create a Trigger, however my attempts have been unsuccessful. I seem to be getting an error (#1064), which I have no solution for. Can somebody explain or demonstrate any faults in the syntax.
Let me specify:
I have delivery_id as primary key in delivery table,
I also have delivery_id as a foreign key in entry_log table.
By comparing both id's(if true), will return a text referring to the output of the bit (either 0 or 1)
DELIMITER //
DROP TRIGGER IF EXISTS entry_trigger//
CREATE TRIGGER entry_trigger BEFORE INSERT ON entry_log
FOR EACH ROW
BEGIN
DECLARE #xentry VARCHAR(45)
DECLARE #inta bit
SET #inta = SELECT allowed
FROM delivery
WHERE delivery.delivery_id = entry_log.delivery_id;
CASE
when #inta = 0 then #xentry = 'Acces Denied'
when #inta = 1 then #xentry = 'Acces Allowed'
END CASE
INSERT INTO entry_log(entry_time,access_allowed) VALUES(now(),#xentry);
END
//
This is assuming that you use MySQL. In the body of the trigger you use
WHERE delivery.delivery_id = entry_log.delivery_id;
I think you want to compare to the entry_log entry that the trigger is running on, right? In that case you must use this syntax:
WHERE delivery.delivery_id = NEW.delivery_id;
see here for more examples.
UPDATE
I see that also you try to do an INSERT INTO entry_log within the TRIGGER. This will of course not work, because you would create an infinite recursive loop. Within the
body of the trigger you can do unrelated table access, but not into the table you are inserting. You can change the values to be inserted by the trigger by setting NEW.xyz = whatever
UPDATE 2
I doubt, that your CASE statement is correct. At least it must end with END CASE. You can use IF here, since you don't have many cases to address. If you must use CASE this post might help you: MYSQL Trigger set datetime value using case statement
UPDATE 3
I am not sure, but I think you need brackets around the variable setting statement. try this trigger definition:
DELIMITER //
DROP TRIGGER IF EXISTS entry_trigger//
CREATE TRIGGER entry_trigger BEFORE INSERT ON entry_log
FOR EACH ROW
BEGIN
SET #inta = (SELECT allowed
FROM delivery
WHERE delivery.delivery_id = NEW.delivery_id);
SET NEW.access_allowed = #inta;
SET NEW.entry_time = NOW();
END
//
Note, that this is written out of my head, so beware of syntax errors in my script.
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