Net-Base Περιοδικό

28.07.2026

FireDAC: Bulk-Insert mit Array DML und sauberem Error-Handling pro Zeile

FireDAC Array DML beschleunigt Bulk-Inserts massiv – bis der erste Constraint-Fehler kommt. Dieser Praxisbeitrag zeigt, wie du Bulk-Insert mit Array DML so baust, dass du pro Zeile robuste Fehlerinfos bekommst, Transaktionen sauber steuerst und im Betrieb sinnvoll debuggen...

28.07.2026

Από το θέμα του περιοδικού στην πρακτική εφαρμογή του έργου

Σχετικές σελίδες υπηρεσιών και τεχνολογίας για το άρθρο

Ein BDE-Ablosung mit nativer Anbindung Bulk-Insert mit Array DML ist oft der schnellste Weg, um viele Datensätze in eine Datenbank zu bekommen: statt tausend einzelner Inserts wird ein Parameter-Array gebunden und in einem Rutsch an den Server geschickt. In der Praxis kommt der Knackpunkt aber schnell: Ein Datensatz verletzt einen Unique-Index, ein NOT NULL-Feld ist leer, ein Foreign Key passt nicht – und plötzlich ist unklar, welche Zeile das Batch gekillt hat, ob ein Teil schon geschrieben wurde und wie du sauber weitermachst, ohne Dateninkonsistenzen zu erzeugen.

Genau darum geht es hier: Wie du Array DML so einsetzt, dass du pro Zeile belastbare Fehlerinfos bekommst, die Transaktion im Griff behältst und im Betrieb nachvollziehen kannst, was passiert ist. Der Fokus liegt nicht auf akademischer API-Lektüre, sondern auf dem Randfall, der in echten Importen regelmäßig aufschlägt: Ein großer Batch, wenige kaputte Zeilen, aber du willst trotzdem Tempo.

FireDAC Bulk-Insert mit Array DML: Warum Array DML beim Bulk-Insert überhaupt lohnt

Passendes Inline-Motiv zum Abschnitt FireDAC Bulk-Insert mit Array DML: Warum Array DML beim Bulk-Insert überhaupt lohnt
Ein passendes Motiv zum Abschnitt "BDE-Ablosung mit nativer Anbindung Bulk-Insert mit Array DML: Warum Array DML beim Bulk-Insert überhaupt lohnt" vertieft den Inhalt visuell.

Array DML (Data Manipulation Language) bedeutet in FireDAC: Du bindest Parameter nicht als Einzelwert, sondern als Array. FireDAC sendet dann (je nach Treiber/DB) weniger Roundtrips, kann serverseitig effizienter arbeiten und reduziert den Overhead im Client drastisch. Das ist in drei Situationen besonders relevant:

  • ETL- und Importstrecken: CSV/XML/JSON rein, Normalisierung/Mapping, dann in Staging- oder Zieltabelle.
  • Schnittstellen-Puffer: REST- oder MQ-Payloads werden gesammelt und periodisch persistiert.
  • Protokoll-/Event-Tabellen: viele kleine Inserts, bei denen Latenz dominiert.

Der Gewinn kommt aber nicht gratis. Mit Array DML verschiebst du Komplexität von „viele einzelne Statements“ zu „ein Statement mit vielen Zeilen“. Das ist gut für Performance, aber anspruchsvoller für Fehlerdiagnose, Transaktionslogik und Wiederanlauf.

Der typische Randfall: Ein Batch, eine kaputte Zeile

Der Klassiker im Betrieb: Du importierst 50.000 Zeilen. Du wählst eine ArraySize von 1.000, weil du nicht für jede Zeile einen Roundtrip willst. Batch 17 scheitert. Die DB meldet nur „duplicate key“ oder „violates foreign key constraint“. In der UI oder im Service-Log steht dann oft nur: „ExecSQL failed“.

Ohne sauberes Error-Handling passieren dann meist zwei schlechte Dinge:

  • Du wirfst den ganzen Batch weg, obwohl 999 von 1.000 Zeilen ok wären.
  • Du fällst auf Einzel-Inserts zurück und verlierst den Performancevorteil dauerhaft.

Ziel ist ein dritter Weg: Batch-Performance behalten, aber defekt-genau (Zeilenindex, Schlüsselwerte, DB-Fehlertext) protokollieren und optional „good rows“ committen – abhängig davon, wie kritisch Konsistenz und Idempotenz (mehrfaches Ausführen ohne doppelte Wirkung) in deinem Prozess sind.

FireDAC Array DML: Die relevanten Stellschrauben (ohne Mythen)

Für Bulk-Insert mit Array DML sind in der Praxis immer dieselben Stellschrauben entscheidend:

1) ArraySize und Batch-Größe

ArraySize (bei TFDQuery/TFDCommand) bestimmt, wie viele „Zeilen“ FireDAC in einem Aufruf abarbeitet. Größer ist nicht automatisch besser. Zu groß bedeutet: mehr Speicher im Client, mehr Payload auf der Leitung, größere Locks/Log-Last auf dem Server und im Fehlerfall mehr „Blast Radius“. Für robuste Imports ist häufig eine Batch-Größe zwischen 200 und 2.000 ein guter Startpunkt, abhängig von Spaltenanzahl, BLOBs und Latenz.

2) Transaktionsgrenze

Du brauchst eine klare Entscheidung: Commit pro Batch oder Commit für den gesamten Import. Das ist keine Geschmackssache, sondern eine Betriebsentscheidung:

  • Commit pro Batch: begrenzt Sperren und Transaktionslog, einfacher Wiederanlauf, aber Zwischenstände sind sichtbar (je nach Isolation Level). Fehler in Batch 17 lassen Batch 1–16 im System.
  • Commit am Ende: „Alles oder nichts“, konsistenter in einem fachlichen Sinne, aber bei großen Mengen riskierst du lange Locks, großes Rollback und im Fehlerfall ist alles weg.

Für viele Schnittstellen- und Importprozesse ist „Commit pro Batch“ die realistischere Betriebsstrategie – aber nur, wenn du Idempotenz und Dublettenstrategie sauber geregelt hast (z. B. über natürliche Schlüssel, Upserts oder eine Import-ID).

3) UpdateOptions und Prepared Statements

Bei wiederholten Batches lohnt es sich, das Statement vorbereitet zu lassen. „Prepare“ bedeutet: FireDAC lässt die DB das Statement parsen/kompilieren und wiederverwendet es. Je nach DB kann das einen spürbaren Effekt haben, vor allem bei hoher Frequenz. Wichtig ist hier weniger „Trick 17“, sondern: Konsequente Wiederverwendung desselben Query-Objekts (oder desselben TFDCommand) und stabile Parametertypen.

Sauberes Error-Handling pro Zeile: Was du wirklich brauchst

Wenn du „pro Zeile“ Fehler behandeln willst, brauchst du drei Dinge:

  1. Zuordnung: Welcher Array-Index (0..N-1) hat versagt?
  2. Kontext: Welche fachlichen Schlüsselwerte hat diese Zeile (z. B. externe ID, Kundennummer, Zeitstempel)?
  3. Steuerung: Was tust du danach? Abbrechen, nur schlechte Zeilen skippen, oder Batch splitten?

FireDAC kann je nach Treiber Fehler je Array-Element zurückliefern. Praktisch ist das aber nicht „einfach immer da“. Du musst damit rechnen, dass manche Datenbanken/Provider nur den ersten Fehler melden oder dass im Batch ein Fehler den Rest gar nicht mehr ausführt. Genau deswegen ist ein robustes Muster meist zweistufig:

  • Stufe A: Versuche den Batch als Array DML.
  • Stufe B: Wenn der Batch scheitert, splitte ihn (halbieren) oder falle kontrolliert auf Einzelzeilen zurück – aber nur für diesen Batch – und logge sauber.

Das klingt nach Mehrarbeit, ist aber in Importstrecken der Unterschied zwischen „nachts um 02:00 Uhr bleibt alles stehen“ und „Import läuft durch, 7 Zeilen landen in der Fehlerliste“.

Ein praxistaugliches Muster: Batch zuerst, dann gezielt isolieren

Das folgende Muster hat sich für prozessnahe Softwarelösungen bewährt, in denen Datenqualität gemischt ist:

Schritt 1: Daten in eine Batch-Struktur packen (inkl. Fehlerkontext)

Speichere die zu importierenden Daten nicht nur als rohe Werte, sondern mit minimalem Kontext: externe ID, Zeilennummer aus der Quelle, eventuell Hash/Checksumme. Das ist nicht „Nice to have“: Im Fehlerfall willst du nicht erst wieder die CSV parsen, um herauszufinden, was kaputt ist.

Schritt 2: Array DML ausführen

Du setzt ArraySize auf Batch-Länge, bindest Parameter als Arrays, und führst ExecSQL aus. Wichtig: Parametertypen stabil halten (z. B. für numerische Felder nicht mal als String, mal als Integer binden), sonst produziert die DB implizite Casts oder FireDAC muss pro Element umwandeln.

Schritt 3: Fehlerfall – Batch eingrenzen statt blind wiederholen

Wenn ExecSQL fehlschlägt, hast du zwei robuste Optionen:

  • Binary Split (halbieren): Batch in zwei Hälften teilen, jede Hälfte erneut als Array DML versuchen. Das wiederholst du, bis du bei einer kleinen Menge bist, die du einzeln prüfen kannst. Vorteil: Du behältst viel Performance, wenn nur wenige Zeilen kaputt sind. Nachteil: Mehr Logik, und bei systematischen Fehlern (z. B. falscher Datentyp) bringt es wenig.
  • Fallback auf Einzelzeilen für diesen Batch: Du setzt ArraySize=1 (oder bindest Einzelwerte) und führst Zeile für Zeile aus, loggst Fehler und machst weiter. Vorteil: Einfach, garantiert pro Zeile. Nachteil: In diesem Batch verlierst du Tempo.

In der Praxis kombiniere ich beides: Erst 1–2 mal splitten (um „gute Blöcke“ schnell durchzubekommen), dann bei kleinen Restmengen auf Einzelzeilen umsteigen, um eindeutige Fehlerinfos zu loggen.

Fehlerobjekte und Meldungen: Was du aus FireDAC herausziehen solltest

FireDAC kapselt DB-Fehler in Exceptions (typisch EFDDBEngineException) mit Detailinformationen. Für den Betrieb sind drei Ebenen wichtig:

  • DB-Fehlercode (db-spezifisch): z. B. SQLSTATE bei PostgreSQL, Error Number bei SQL Server.
  • Constraint-/Objektname: häufig im Fehlertext enthalten (Unique-Index, FK-Constraint).
  • Statement-Kontext: Tabelle, Operation, ggf. Parameterwerte (vorsichtig bei personenbezogenen Daten).

Wenn du pro Zeile loggen willst, musst du im Fehlerfall außerdem die Zeile identifizieren. FireDAC kann unter Umständen den Array-Index liefern. Verlass dich aber nicht ausschließlich darauf. Baue dir immer zusätzlich einen eigenen Index (Position im Batch) und logge zu dieser Position mindestens einen fachlichen Schlüssel.

Fallstricke, die in echten Imports Zeit kosten

1) „Es war doch nur eine Zeile“ – aber die Transaktion ist schon „dirty“

Je nach DB und Treiber kann ein Fehler dazu führen, dass die gesamte Statement-Ausführung als fehlgeschlagen gilt und die Transaktion in einem Zustand ist, in dem du entweder explizit rollen musst oder in dem weitere Statements scheitern. Gerade bei manchen Treibern ist „nach Fehler einfach weitermachen“ keine sichere Annahme.

Konsequenz: Wenn du in einer Transaktion arbeitest und ein Batch fehlschlägt, ist der Standardpfad: Rollback des aktuellen Batch-Kontexts (oder der gesamten Transaktion) und dann neu ansetzen. Das passt gut zu „Commit pro Batch“.

2) Autocommit vs. explizite Transaktion

Wenn du keine explizite Transaktion startest, entscheidet oft der Treiber/Provider, wie er Statements committet. Für Bulk-Imports ist das selten das, was du willst. Explizite Transaktionen geben dir Kontrolle über:

  • Lock-Dauer
  • Rollback-Verhalten
  • Wiederanlaufpunkte

Und: Explizit heißt nicht „eine riesige Transaktion“. Es heißt „bewusst“.

3) Trigger, Constraints und Nebenwirkungen

Array DML beschleunigt die Übergabe, aber nicht automatisch die serverseitige Arbeit. Wenn du auf der Zieltabelle Trigger hast (z. B. Audit-Logging, automatische Statusberechnung), dann ist der Engpass eventuell gar nicht der Insert, sondern der Triggercode. Dann kann ein Batch zwar weniger Roundtrips haben, aber die CPU auf dem DB-Server bleibt der limitierende Faktor.

Für Admins und technische Leads: Bei Performanceproblemen lohnt ein Blick auf Wait Events/Locks und das Transaktionslog. Der Bulk-Insert ist dann nur der Auslöser, nicht die Ursache.

4) Datentypen und implizite Konvertierungen

Einer der häufigsten „Warum ist das langsam?“–Gründe: Parameter werden als String gebunden, die DB castet pro Zeile zu Integer/Date/Decimal. Das ist unsichtbar, aber teuer. Für stabile Performance:

  • Parameter-Datentypen passend setzen (Datum als Datum, Zahl als Zahl).
  • Bei Decimals auf Locale-Fallen achten (Komma vs. Punkt). FireDAC ist hier meist korrekt, aber Mischquellen sind es nicht.
  • Zeitzonen/UTC-Strategie vorab klären (Timestamps sind bei Imports ein Klassiker).

5) Fehlertexte sind für Menschen, aber nicht für Automatisierung

Es ist verführerisch, den Fehlertext zu parsen („duplicate key value violates unique constraint …“). Mach das nur als letzte Option. Besser sind strukturierte Codes (SQLSTATE, Error Number). Leider liefern nicht alle Treiber alles gleich gut. Plane daher beides ein: Code und Text, plus optional „Constraint-Name aus Text“, aber ohne harte Abhängigkeit.

Debugging-Hinweise: So findest du schnell die kaputte Zeile

Batch reproduzierbar machen

Wenn ein Import sporadisch scheitert, brauchst du Reproduzierbarkeit. Speichere pro Batch eine kleine Diagnose-Datei oder einen Logeintrag, der enthält:

  • Batch-Nummer und Zeit
  • ArraySize und Transaktionsmodus
  • die Liste der fachlichen Schlüssel (z. B. externe IDs) im Batch

Das reicht oft, um im Nachgang gezielt einen Mini-Import nur für diese IDs zu starten.

Finale SQL sichtbar machen (aber ohne Datenleaks)

Im Debugging willst du wissen: Ist die SQL korrekt? Sind die Parameter richtig? FireDAC bietet Monitoring/Tracing über FDMoni-Komponenten und Treiber-Logging. In produktionsnahen Umgebungen ist wichtig:

  • Tracing gezielt nur temporär aktivieren (Performance und Datenschutz).
  • Parameterwerte nur in einer sicheren Umgebung oder maskiert loggen.
  • Bei personenbezogenen Daten: im Log nur technische Schlüssel (IDs) und keine Klartextinhalte.

Wenn du split-testest: Abbruchkriterien definieren

Beim Binary Split willst du nicht endlos teilen. Setze eine Untergrenze, z. B. „unter 20 Zeilen switch auf Einzelmodus“. Und setze ein Limit, wie viele Fehler du insgesamt tolerierst, bevor du den Import abbrichst (z. B. bei systematischen Mapping-Problemen). Sonst läufst du in endlose Fehlerlisten und blockierst Folgeverarbeitung.

Wann sich der Aufwand wirklich lohnt (und wann nicht)

Array DML mit pro Zeile Error-Handling lohnt sich besonders, wenn:

  • Viele Zeilen verarbeitet werden (tausende bis Millionen).
  • Wenige Zeilen fehlerhaft sind, aber du trotzdem durchlaufen willst.
  • Import im Betrieb stabil laufen muss (z. B. Nachtverarbeitung, Service ohne UI).
  • Du eine Fehlerliste an Fachbereich/Quelle zurückspielen musst (mit Zeilenbezug).

Es lohnt sich weniger, wenn:

  • du nur ein paar Dutzend Zeilen schreibst (Einzelinserts sind ok),
  • die Datenqualität so schlecht ist, dass 30–50% der Zeilen scheitern (dann ist eine Staging-Strategie sinnvoller),
  • du ohnehin ein DB-natives Bulk-Load-Verfahren nutzt (z. B. COPY in PostgreSQL, BCP/BULK INSERT in SQL Server) – dann ist Array DML nicht das Werkzeug.

Alternative Architektur: Staging-Tabelle statt „Direkt ins Ziel“

Wenn du regelmäßig mit gemischter Datenqualität kämpfst, ist ein reines „Insert direkt in die Zieltabelle“ oft die falsche Entscheidung. Eine Staging-Tabelle (Vorstufe) ist eine Tabelle, in der du Daten zunächst technisch korrekt speicherst (ggf. mit weichen Typen), und erst danach validierst und in die Zieltabelle überführst.

Vorteile im Betrieb:

  • Fehlerhafte Datensätze bleiben nachvollziehbar gespeichert (inkl. Rohdaten).
  • Du kannst Validierung separat und wiederholbar ausführen.
  • Du entkoppelst Schnittstellenannahme von fachlicher Verarbeitung.

Array DML ist dann oft der schnelle Weg in die Staging-Tabelle, während die Überführung in die Zieltabelle als set-basierte SQL (oder Stored Procedure) passiert. Das verschiebt Fehlerbehandlung stärker auf DB-Seite, was je nach Organisation (DBA-Rollen, Deployment) sinnvoll oder unerwünscht sein kann.

Betrieb und Administration: Was IT-Leads und Admins dazu wissen sollten

Monitoring: Fehlerquote und Durchsatz sind die Kernmetriken

Für den stabilen Betrieb eines Bulk-Imports sind zwei Metriken aussagekräftiger als „Laufzeit“ alleine:

  • Durchsatz: Zeilen pro Minute (oder pro Batch) inkl. Peak/Median.
  • Fehlerquote: fehlerhafte Zeilen pro Lauf, ideal nach Fehlerklassen gruppiert (Unique, FK, NOT NULL, Typkonflikt).

Wenn du diese beiden Werte regelmäßig siehst, erkennst du früh, ob sich an der Quelle etwas geändert hat (z. B. neues Format) oder ob das Zielsystem (z. B. neue Constraints) strenger geworden ist.

Sperren und Lastfenster

Bulk-Inserts können Locking und IO-Last erzeugen. Wenn parallel Anwender auf denselben Tabellen arbeiten, musst du über Isolation Level, Indizes und ggf. Partitionierung nachdenken. Praktisch heißt das: Entweder Imports in Lastfenster legen, oder den Datenfluss so bauen, dass er mit laufendem Betrieb koexistiert (z. B. über Staging + asynchrone Übernahme).

Konkrete Checkliste für einen robusten Bulk-Insert mit Array DML

  • Batch-Größe festlegen (Startwert 500–1.000) und messbar tunen.
  • Explizite Transaktion: Commit pro Batch als Default, „Commit am Ende“ nur bewusst.
  • Parametertypen stabil setzen, keine impliziten Casts erzwingen.
  • Fehlerkontext pro Datensatz mitführen (externe ID, Quellzeile).
  • Fehlerstrategie: Batch zuerst, dann Split/Fallback, pro Zeile loggen.
  • Logging: Codes + Text, aber datenschutzkonform; Batch-ID und Lauf-ID erfassen.
  • Wiederanlauf: Idempotenz sicherstellen (Schlüssel/Upsert/Import-ID).

Fazit: Array DML ist schnell – robust wird es durch Prozess und Fehlerstrategie

Ein FireDAC Bulk-Insert mit Array DML ist ein starkes Werkzeug, solange du nicht so tust, als gäbe es keine Fehler. In echten Datenströmen gibt es immer Ausreißer: Dubletten, fehlende Referenzen, kaputte Datumswerte. Der saubere Ansatz ist deshalb: Array DML für die Performance, kombiniert mit einer kontrollierten Isolationsstrategie (Split oder Fallback) und einer pro Zeile nachvollziehbaren Fehlerliste. Damit bekommst du Tempo und Betriebssicherheit zusammen – und genau das zählt, wenn Imports nicht nur im Lab laufen, sondern jede Nacht zuverlässig durch müssen.

Wenn ihr einen bestehenden Import- oder Schnittstellenprozess in Delphi/FireDAC stabilisieren wollt (Performance, Transaktionen, Wiederanlauf, Logging), klären wir das gern strukturiert im technischen Gespräch:

Für dieses Thema sind auch Delphi Bulk Insert und Bulk Insert Delphi FireDAC wichtig. Der Beitrag ordnet diese Aspekte verständlich ein und zeigt, worauf es im Alltag ankommt.

Projekt oder Modernisierungsvorhaben mit Net-Base besprechen.

επόμενο βήμα

Wenn aus dem Thema ein reales Projekt wird, sollten Architektur, Bestand und Betrieb früh zusammen betrachtet werden.

Υποστηρίζουμε όχι μόνο σε μεμονωμένα ζητήματα, αλλά και όταν από αποσπάσματα πηγαίου κώδικα, θέματα legacy ή ιδέες για πύλες πρέπει να προκύψει ένα αξιόπιστο εταιρικό έργο.

  • Η υφιστάμενη κατάσταση, το επιθυμητό μελλοντικό μοντέλο και οι τεχνικοί κίνδυνοι αξιολογούνται από κοινού.
  • REST, πρόσβαση στα δεδομένα, πύλες και Rollout δεν θα αναβληθούν ως μεταγενέστερες συνέπειες.
  • Διαπιστώνετε έγκαιρα ποια προσέγγιση είναι οικονομικά και επιχειρησιακά βιώσιμη.

Κοινοποίηση δημοσίευσης

Μοιραστείτε αυτήν την ανάρτηση απευθείας

LinkedIn, X, XING, Facebook, WhatsApp und E-Mail sind sofort verfügbar. für Instagram bereiten wir Link und Kurztext direkt vor.

Ηλεκτρονικό ταχυδρομείο

Το Instagram ανοίγει σε μια νέα καρτέλα. Ο σύνδεσμος και το σύντομο κείμενο αντιγράφονται πρώτα στο πρόχειρο.