The basic problem you have is that everything is recursive and you are attempting to lock pages of data in InnoDB, creating lots of [**MVCC**][1] info. In some case, you may have attempted to lock the same InnoDB page repeatedly. That explains the deadlocks. I have commented on this situation deeply with someone in three separate posts on this same issue: - https://dba.stackexchange.com/questions/3179/trouble-deciphering-a-deadlock-in-an-innodb-status-log/3185#3185 - https://dba.stackexchange.com/questions/3223/will-these-two-queries-result-in-a-deadlock-if-executed-in-sequence/3224#3224 - https://dba.stackexchange.com/questions/3302/reasons-for-occasionally-slow-queries/3305#3305 You need a workaround. You may want to try the following algorithm: For this example - id = 12 - amount = 22.75 Step 01) Make a table to collect all father_ids to be updated DROP TABLE IF EXISTS fathers_to_update; CREATE TABLE fathers_to_update ( father_id INT NOT NULL, PRIMARY KEY (father_id) ); Step 02) Making a table that will be a FIFO queue. Put the value of 12 into the queue: DROP TABLE IF EXISTS fathers_to_queue; CREATE TABLE fathers_to_queue ( father_id INT, id INT NOT NULL AUTO_INCREMENT, PRIMARY KEY (id) ); INSERT INTO fathers_to_queue (father_id) VALUES (12); Step 03) Get the Count of `fathers_to_queue` SELECT COUNT(1) FROM fathers_to_queue; If the count is 0, goto Step 07) Step 04) Remove the front number in `fathers_to_queue` SELECT MIN(id),father_id INTO @frontndx,@dad FROM fathers_to_queue; DELETE FROM fathers_to_queue WHERE id = @frontndx; Step 05) Collect all father_ids from 161_ft_individual whose id is @dad into the queue INSERT INTO fathers_to_update SELECT father_id FROM 161_ft_individual WHERE id = @dad; INSERT INTO fathers_to_queue (father_id) SELECT father_id FROM 161_ft_individual WHERE id = @dad; Step 06) Go back to Step 03 Step 07) Perform a single UPDATE of all records in 161_ft_individual you collected in `fathers_to_update` UPDATE fathers_to_update A LEFT JOIN 161_ft_individual B USING (father_id) SET B.total_leg=B.total_leg+@amount; That's it. I basically performed a preoder tree traversal back up the hierarchy to all records until father_id was NULL, which makes Step 03 stop collecting father_ids. Give it a Try !!! **UPDATE 2011-12-16 12:18 EDT** Back on October 24, 2011, I actually wrote an algorithm in the MySQL Stored Procedure language to perform preorder tree traversal: https://dba.stackexchange.com/questions/7147/find-highest-level-of-a-hierarchical-field-with-vs-without-ctes/7161#7161. Hope it helps !!! [1]: http://en.wikipedia.org/wiki/Multiversion_concurrency_control