diff --git a/lib/gitlab/database/migration_helpers.rb b/lib/gitlab/database/migration_helpers.rb
index 909ff8677cb2fbd01186f02d90c0656241ffc955..dec20d8659b1559a13abbae577db0b500d4a6b3d 100644
--- a/lib/gitlab/database/migration_helpers.rb
+++ b/lib/gitlab/database/migration_helpers.rb
@@ -30,7 +30,7 @@ module Gitlab
       # This method updates the table in batches of 5% of the total row count.
       # This method will continue updating rows until no rows remain.
       #
-      # When given a block this method will yield to values to the block:
+      # When given a block this method will yield two values to the block:
       #
       # 1. An instance of `Arel::Table` for the table that is being updated.
       # 2. The query to run as an Arel object.
@@ -44,59 +44,63 @@ module Gitlab
       #       query.where(table[:some_column].eq('hello'))
       #     end
       #
-      # This would result in this method updating only rows there
+      # This would result in this method updating only rows where
       # `projects.some_column` equals "hello".
       #
       # table - The name of the table.
       # column - The name of the column to update.
       # value - The value for the column.
+      #
+      # Rubocop's Metrics/AbcSize metric is disabled for this method as Rubocop
+      # determines this method to be too complex while there's no way to make it
+      # less "complex" without introducing extra methods (which actually will
+      # make things _more_ complex).
+      #
+      # rubocop: disable Metrics/AbcSize
       def update_column_in_batches(table, column, value)
         table = Arel::Table.new(table)
-        processed = 0
 
         count_arel = table.project(Arel.star.count.as('count'))
         count_arel = yield table, count_arel if block_given?
 
         total = exec_query(count_arel.to_sql).to_hash.first['count'].to_i
 
+        return if total == 0
+
         # Update in batches of 5% until we run out of any rows to update.
         batch_size = ((total / 100.0) * 5.0).ceil
 
-        loop do
-          start_arel = table.project(table[:id]).
-            order(table[:id].asc).
-            take(1).
-            skip(processed)
-
-          start_arel = yield table, start_arel if block_given?
-          start_row = exec_query(start_arel.to_sql).to_hash.first
-
-          # There are no more rows to process
-          break unless start_row
+        start_arel = table.project(table[:id]).order(table[:id].asc).take(1)
+        start_arel = yield table, start_arel if block_given?
+        start_id = exec_query(start_arel.to_sql).to_hash.first['id'].to_i
 
+        loop do
           stop_arel = table.project(table[:id]).
+            where(table[:id].gteq(start_id)).
             order(table[:id].asc).
             take(1).
-            skip(processed + batch_size)
+            skip(batch_size)
 
           stop_arel = yield table, stop_arel if block_given?
           stop_row = exec_query(stop_arel.to_sql).to_hash.first
 
-          update_manager = Arel::UpdateManager.new(ActiveRecord::Base)
-
-          update_arel = update_manager.table(table).
+          update_arel = Arel::UpdateManager.new(ActiveRecord::Base).
+            table(table).
             set([[table[column], value]]).
-            where(table[:id].gteq(start_row['id']))
-
-          update_arel = yield table, update_arel if block_given?
+            where(table[:id].gteq(start_id))
 
           if stop_row
-            update_arel = update_arel.where(table[:id].lt(stop_row['id']))
+            stop_id = stop_row['id'].to_i
+            start_id = stop_id
+            update_arel = update_arel.where(table[:id].lt(stop_id))
           end
 
+          update_arel = yield table, update_arel if block_given?
+
           execute(update_arel.to_sql)
 
-          processed += batch_size
+          # There are no more rows left to update.
+          break unless stop_row
         end
       end