13. Oktober 2026
Was update_all tatsächlich macht
update_all ist in Rails kein beschleunigtes update für viele Datensätze. Es ist ein anderer Mechanismus: ActiveRecord erzeugt ein einzelnes SQL-UPDATE für die Relation und führt es direkt in der Datenbank aus.
Dabei werden keine einzelnen Modellinstanzen geladen. Entsprechend gibt es keine Instanz pro Zeile, auf der Rails Validierungen oder Callbacks ausführen könnte. Genau darin liegt der Nutzen: Die Änderung bleibt auf Datenbankebene und vermeidet den Aufwand, viele Ruby-Objekte zu erzeugen und einzeln zu speichern.
Ein typisches Beispiel:
User.where(active: false).update_all(status: "archived")Das aktualisiert alle Datensätze im Scope mit einem UPDATE. Es entspricht nicht diesem Ablauf:
User.where(active: false).find_each do |user|
user.update(status: "archived")
endDie zweite Variante lädt Modelle und speichert jedes einzeln. Sie folgt damit dem normalen ActiveRecord-Lebenszyklus. Die erste Variante nicht.
Was dabei bewusst ausbleibt
Bei Rails update_all laufen weder Modellvalidierungen noch Callbacks. Das betrifft etwa before_update, after_update, after_save und ähnliche Mechanismen. Auch updated_at wird nicht automatisch gesetzt.
Das ist besonders wichtig, wenn Geschäftslogik in Callbacks steckt. Angenommen, ein after_update aktualisiert einen Suchindex oder schreibt einen Audit-Eintrag. Ein update_all löst diese Logik nicht aus. Der Datenbankwert ändert sich, aber die erwartete Nebenwirkung bleibt aus.
Dasselbe gilt für ActiveRecord update_all Validierungen: Eine Validierung, die bei model.update einen ungültigen Zustand verhindern würde, wird bei update_all nicht ausgeführt. Die Datenbank ist dann die letzte Schutzschicht. Datenbank-Constraints bleiben wirksam, Modellvalidierungen dagegen nicht.
Bei Zeitstempeln ist der Effekt häufig subtil. Wenn ein Cache, ein Export oder ein Synchronisationsprozess auf Rails updated_at reagiert, kann eine Änderung unsichtbar bleiben, wenn der Zeitstempel unverändert ist. Die Fachdaten sind neu, für nachgelagerte Prozesse sieht der Datensatz aber alt aus.
Wann update_all passt
update_all ist passend, wenn eine klar abgegrenzte Massenänderung keine Modelllogik benötigt. Typische Fälle sind Backfills, das Setzen eines einfachen Status oder das Aktualisieren von Zählern, sofern daran keine Callback-Logik hängt.
Auch bei großen Datenmengen ist der direkte SQL-Weg oft sinnvoll. Entscheidend ist nicht allein die Menge, sondern die Frage: Soll für jeden Datensatz der normale Lebenszyklus des Modells laufen? Wenn die Antwort nein lautet, ist ein einzelnes UPDATE häufig das passendere Werkzeug.
Zeitstempel sollten dann bewusst behandelt werden:
User.where(active: false).update_all(
status: "archived",
updated_at: Time.current
)Damit wird nicht plötzlich ein Callback ausgelöst. Aber die Änderung ist wenigstens über updated_at erkennbar. Ob das korrekt ist, hängt von der Semantik der Anwendung ab: Ein Zeitstempel sollte eine fachlich relevante Änderung abbilden, nicht nur einen technischen Reflex.
Wo es gefährlich wird
Gefährlich wird update_all, wenn der Code still Annahmen des Modells umgeht. Prüft insbesondere diese Abhängigkeiten:
- Callbacks, die Caches invalidieren oder abhängige Daten aktualisieren
- Suchindizes, die nach Modelländerungen aktualisiert werden
- Audit-Logs, die Änderungen über Callbacks erfassen
- Logik, die auf
updated_atfür Synchronisation, Cache-Invalidierung oder Exporte setzt - Validierungen, die nur im Rails-Modell und nicht zusätzlich in der Datenbank abgesichert sind
Auch der Scope verdient besondere Aufmerksamkeit. Ein zu breiter Scope verändert nicht einen Datensatz falsch, sondern potenziell viele. Die WHERE-Bedingung sollte deshalb vor der Änderung lesbar, testbar und fachlich eindeutig sein.
Wenn Callbacks nötig sind
Müssen Validierungen, Callbacks oder modellgebundene Nebenwirkungen laufen, ist die normale Aktualisierung die passendere Wahl. Bei vielen Datensätzen lässt sich das in Batches organisieren:
User.where(active: false).find_each do |user|
user.update(status: "archived")
endDas ist bewusst etwas anderes als update_all: Jede Zeile durchläuft den Modelllebenszyklus. Falls die Datenmenge begrenzt werden soll, kann der Scope zusätzlich in Batches aufgeteilt werden. Wichtig ist, die Semantik nicht für vermeintliche Geschwindigkeit zu verlieren.
update_all ist also weder ein Rails-Fehler noch ein Trick. Es ist direkter SQL-Zugriff durch ActiveRecord. Genau deshalb sollte jede Verwendung eine klare Antwort auf zwei Fragen haben: Welche Modelllogik wird absichtlich übersprungen, und muss updated_at mitgeschrieben werden?
Prüft, wo in eurer Codebase update_all steht und ob dahinter Callbacks oder updated_at-Logik hängen.