Net-Base Revistë

28.07.2026

FireDAC: Bulk-Insert me Array-DML dhe menaxhim të qartë të gabimeve për çdo rresht

FireDAC Array DML përshpejton masivisht Bulk-Inserts – deri kur të ndodhë gabimi i parë i constraint-it. Ky artikull praktik tregon si të ndërtosh Bulk-Insert me Array DML në mënyrë që të marrësh për çdo rresht informacione të qarta dhe të besueshme për gabimet, të menaxhosh transaksionet në mënyrë të saktë dhe të debugosh në mënyrë të dobishme gjatë operimit...

28.07.2026

Nga tema e revistës në praktikën e projektit

Faqe shërbimi dhe teknike të përshtatshme për artikullin

Një BDE-Ablosung mit nativer Anbindung Bulk-Insert me Array DML është shpesh mënyra më e shpejtë për të futur shumë rekorde në një bazë të dhënash: në vend që të ekzekutohen mijëra INSERT-e të veçanta, lidhet një varg parametrash dhe dërgohet njëherësh te serveri. Në praktikë problemi shfaqet shpejt: një rekord shkel një indeks unik, një fushë NOT NULL është bosh, një Foreign Key nuk përputhet – dhe papritmas nuk është e qartë cilën rresht e ka prishur batch-in, nëse një pjesë është tashmë shkruar dhe si të vazhdosh në mënyrë të pastër pa shkaktuar inkonsistenca të dhënash.

Pikërisht për këtë bëhet fjalë këtu: Si të përdorësh Array DML që të marrësh informacione të besueshme gabimesh për çdo rresht, të mbash transaksionin nën kontroll dhe të mund të gjurmosh në prodhim se çfarë ndodhi. Fokusimi nuk është te lektura akademike e API-së, por te skenari skajor që në importa reale shfaqet rregullisht: Një batch i madh, pak rreshta të prishura, por ti prapë dëshiron shpejtësi.

FireDAC Bulk-Insert me Array DML: Pse Array DML ia vlen në Bulk-Insert

Passendes Inline-Motiv zum Abschnitt FireDAC Bulk-Insert mit Array DML: Warum Array DML beim Bulk-Insert überhaupt lohnt
Një motiv i përshtatshëm për seksionin "BDE-Ablosung mit nativer Anbindung Bulk-Insert me Array DML: Pse Array DML ia vlen në Bulk-Insert" e thellon përmbajtjen në mënyrë vizuale.

Array DML (Data Manipulation Language) do të thotë në FireDAC: ti lidh parametrat jo si vlerë të vetme, por si një array. FireDAC dërgon pastaj (sipas driver/DB) më pak roundtrips, mund të punojë më efikas në server dhe redukton overhead-in në klient në mënyrë drastike. Kjo është veçanërisht relevante në tri situata:

  • Rrugët ETL dhe importit: CSV/XML/JSON hyrje, normalizim/mapim, pastaj në tabelën e staging-ut ose në tabelën e destinacionit.
  • Buffere të ndërfaqeve: REST- ose MQ-payloads mblidhen dhe persistohen periodikisht.
  • Tabelat e protokolleve/eventeve: shumë INSERT-e të vogla, ku dominon latenca.

Fitimi nuk vjen falas. Me Array DML ti zhvendos kompleksitetin nga „shumë deklarata të veçanta“ drejt „një deklarate me shumë rreshta“. Kjo është e mirë për performancën, por kërkon më shumë për diagnostikën e gabimeve, logjikën e transaksionit dhe rifillimin.

Rasti tipik: Një batch, një rresht i prishur

Klasiçi në prodhim: Importon 50.000 rreshta. Zgjedh një ArraySize prej 1.000, sepse nuk dëshiron një roundtrip për çdo rresht. Batch 17 dështon. DB-ja raporton vetëm „duplicate key“ ose „violates foreign key constraint“. Në UI ose në logun e shërbimit shpesh shfaqet vetëm: „ExecSQL failed“.

Pa një trajtim të rregullt të gabimeve, zakonisht ndodhin dy gjëra të këqija:

  • Hedh gjithë batch-in poshtë, edhe pse 999 nga 1.000 rreshta do të ishin në rregull.
  • Kthehesh te INSERT-e të veçanta dhe humb përfitimin e performancës në mënyrë të përhershme.

Qëllimi është një rrugë e tretë: të ruash performancën e batch-it, por të regjistrosh me saktësi defektet defekt-genau (indeksi i rreshtit, vlerat e çelësave, teksti i gabimit të DB-së) dhe opsionalisht të bësh commit të „good rows“ – varësisht nga sa kritike janë konsistenca dhe idempotenca (ekzekutim i përsëritur pa efekt të dyfishtë) në procesin tuaj.

FireDAC Array DML: Parametrat kyç (pa mite)

Për Bulk-Insert me Array DML, në praktikë të njëjtat parametra janë vendimtarë:

1) ArraySize dhe madhësia e batch-it

ArraySize (në TFDQuery/TFDCommand) përcakton se sa „rreshta“ FireDAC përpunohen në një thirrje. Më i madh nuk është automatikisht më i mirë. Përmasë e tepërt do të thotë: më shumë memorie te klienti, më shumë payload në lidhje, bllokime/log i transaksioneve më i madh në server dhe në rast gabimi më i madh „Blast Radius“. Për importe të qëndrueshme, shpesh një madhësi batch midis 200 dhe 2.000 është një pikëfillestare e mirë, varësisht nga numri i kolonave, BLOB-et dhe latenca.

2) Kufiri i transaksionit

Ju keni nevojë për një vendim të qartë: Commit për çdo batch ose Commit për të gjithë importin. Kjo nuk është çështje shijeje, por një vendim operativ:

  • Commit për çdo batch: kufizon bllokimet dhe logun e transaksioneve, e bën rindërtimin/ririnisjen më të thjeshtë, por shtetet ndërmjetëse janë të dukshme (varësisht nga niveli i izolimit). Gabimet në Batch 17 lënë Batch 1–16 në sistem.
  • Commit në fund: „të gjitha ose asgjë“, më konsistent në kuptim funksional, por me sasi të mëdha rrezikoni bllokime të gjata, rollback të madh dhe në rast gabimi gjithçka humbet.

Për shumë procese ndërfaqesh dhe importi, „Commit për çdo batch“ është strategjia operative më realiste – por vetëm nëse keni rregulluar qartë idempotencën dhe strategjinë për dublikatat (p.sh. përmes çelësave natyrorë, Upserts ose një Import-ID).

3) UpdateOptions dhe Prepared Statements

Tek batch-et e përsëritura ia vlen të mbani statement-in të përgatitur. „Prepare“ do të thotë: FireDAC lejon DB-në të parsojë/kompilojë statement-in dhe ta riperdojë atë. Varësisht nga DB-ja, kjo mund të ketë një efekt të dukshëm, veçanërisht në frekuenca të larta. Më pak është fjala për „trik 17“, dhe më shumë për: përdorim konsekuent i të njëjtit objekt Query (ose i të njëjtit TFDCommand) dhe tipe parametrash të qëndrueshme.

Trajtim i pastër i gabimeve për çdo rresht: Çfarë ju duhet realisht

Nëse doni të trajtoni gabimet „për rresht“, ju duhen tre gjëra:

  1. Përcaktim: Cili indeks i array-it (0..N-1) dështoi?
  2. Kontekst: Çfarë vlerash kyçe funksionale ka ky rresht (p.sh. ID e jashtme, numri i klientit, timestamp)?
  3. Vendimmarrja: Çfarë bëni më pas? Anuloni, thjesht kaloni rreshtat e këqij, apo ndani batch-in?

FireDAC mund, varësisht nga driverti, të kthejë gabime për secilin element të array-it. Praktikisht kjo nuk është gjithmonë e disponueshme. Duhet të prisni që disa baza të dhënash/provider të raportojnë vetëm gabimin e parë ose që një gabim në batch të ndalojë pjesën e mbetur. Pikërisht për këtë arsye një model i qëndrueshëm zakonisht është në dy nivele:

  • Niveli A: Provoni batch-in si Array DML.
  • Niveli B: Nëse batch-i dështon, ndani atë (gjysmojeni) ose kaloni në mënyrë të kontrolluar te rreshtat individualë – por vetëm për atë batch – dhe regjistroni qartë.

Kjo tingëllon si punë shtesë, por në rrjedhat e importit është dallimi midis „në orën 02:00 e natës gjithçka ndalet“ dhe „importi vazhdon, 7 rreshta përfundojnë në listën e gabimeve“.

Një model praktik: së pari batch, pastaj izolim i synuar

Ky model ka rezultuar i suksesshëm për zgjidhjet softuerike të lidhura me proceset, ku cilësia e të dhënave është e përzjerë:

Hapi 1: Paketoni të dhënat në një strukturë batch (përfshirë kontekstin e gabimit)

Ruaj të dhënat për import jo vetëm si vlera të papërpunuara, por me kontekst minimal: ID e jashtme, numri i rreshtit nga burimi, eventualisht Hash/Checksum. Kjo nuk është „Nice to have“: Në rast gabimi nuk do të dëshiroje të duhej të parse-je përsëri CSV-në për të zbuluar se çfarë është prishur.

Hapi 2: Ekzekuto Array DML

Vendos ArraySize në gjatësi të batch-it, lidh parametrat si vargje (Arrays) dhe ekzekuto ExecSQL. E rëndësishme: mbaji tipat e parametrave të qëndrueshëm (p.sh. për fusha numerike mos i lidh herë si String, herë si Integer), përndryshe DB prodhon cast-e implicite ose FireDAC duhet të bëjë konvertim për çdo element.

Hapi 3: Rast i gabimit – kufizo batch-in në vend që ta përsëritësh verbërisht

Nëse ExecSQL dështon, ke dy opsione të qëndrueshme:

  • Binary Split (përgjysmë): ndaje batch-in në dy gjysma, provo secilën gjysmë sërish si Array DML. E përsërit këtë derisa të mbërrish tek një sasi e vogël që mund ta verifikosh individualisht. Avantazh: ruan shumë performancë kur vetëm pak rreshta janë të prishura. Disavantazh: kërkon logjikë më tepër, dhe në gabime sistematike (p.sh. tip i gabuar i të dhënave) sjell pak përfitim.
  • Fallback auf Einzelzeilen për këtë batch: vendos ArraySize=1 (ose lidh vlerat një e nga një) dhe ekzekuto rresht pas rreshti, log-o gabimet dhe vazhdo. Avantazh: e thjeshtë, garanton trajtim për çdo rresht. Disavantazh: në këtë batch humb ritmin.

Objektet dhe mesazhet e gabimit: Çfarë duhet të nxjerrësh nga FireDAC

FireDAC kapslon gabimet e DB-së në Exceptions (tipikisht EFDDBEngineException) me informacione detajesh. Për operimin janë të rëndësishme tre nivele:

  • Kodi i gabimit i DB-së (specifik për DB): p.sh. SQLSTATE te PostgreSQL, Error Number te SQL Server.
  • Emri i constraint-/objektit: shpesh i përfshirë në tekstin e gabimit (Unique-Index, FK-Constraint).
  • Konteksti i statement-it: tabela, operacioni, eventualisht vlerat e parametrave (kujdes me të dhënat personale).

Nëse dëshiron të log-osh për rresht, në rast gabimi duhet edhe të identifikosh rreshtin. FireDAC në disa raste mund të japë indeksin e Array-it. Megjithatë mos mbështetesh vetëm tek ai. Ndërto gjithmonë një indeks tënd shtesë (pozicioni në batch) dhe log-o për atë pozicion së paku një çelës profesional.

Kurthet që në importet reale konsumojnë kohë

1) „Ishte vetëm një rresht“ – por transaksioni është tashmë „dirty“

Sipas DB-së dhe driver-it, një gabim mund të bëjë që e gjitha ekzekutimi i statement-it të konsiderohet i dështuar dhe transaksioni të jetë në një gjendje ku duhet të bësh eksplicit rollback ose ku statement-et e mëtejshme dështojnë. Sidomos me disa driverë, premisa „pas gabimit vazhdo“ nuk është e sigurt.

Pasojë: Nëse punon brenda një transaksioni dhe një batch dështon, rruga standarde është: Rollback i kontekstit aktual të batch-it (ose i gjithë transaksionit) dhe pastaj rinis. Kjo përshtatet mirë me „Commit pro Batch“.

2) Autocommit vs. explizite Transaktion

Nëse nuk nis një transaksion eksplicit, shpesh driver-i/provider-i vendos se si committ-et statement-et. Për Bulk-Imports kjo rrallë është ajo që do. Transaksionet eksplizite të japin kontroll mbi:

  • Kohëzgjatjen e bllokimit
  • Sjellja e rollback
  • Pikat e rifillimit
  • Dhe: Eksplizit nuk do të thotë „një transaksion i madh“. Do të thotë „i qëllimshëm“.

    3) Trigger, Constraints dhe efekte anësore

    Array DML përshpejton dorëzimin, por jo automatikisht punën në server. Nëse në tabelën e synuar ke trigger-e (p.sh. audit-logging, llogaritje automatike të statusit), atëherë ngushtica mund të mos jetë Insert-i, por kodi i trigger-it. Atëherë një batch mund të ketë më pak roundtrips, por CPU-ja e DB-Server-it mbetet faktori kufizues.

    Për adminët dhe technical Leads: Në rast problemesh me performancën ia vlen të kontrollosh Wait Events/Locks dhe logun e transaksioneve. Bulk-Insert është atëherë vetëm shkaktari, jo shkaku.

    4) Llojet e të dhënave dhe konvertimet implizite

    Një nga arsyet më të shpeshta për pyetjen „Pse është e ngadaltë?“: parametrat lidhen si string, DB-ja konverton për çdo rresht në Integer/Date/Decimal. Kjo është e padukshme, por e shtrenjtë. Për performancë të qëndrueshme:

    • Vendosni tipet e parametrave në mënyrë të përshtatshme (datë si datë, numër si numër).
    • Për decimalet kini kujdes me kurthet e locale-it (presja vs pika). FireDAC zakonisht është i saktë këtu, por burimet e përziera nuk janë.
    • Sqaroni paraprakisht strategjinë për zonat kohore/UTC (timestamps janë një klasik në importime).

    5) Tekstet e gabimeve janë për njerëzit, por jo për automatizimin

    Është joshëse të parsosh tekstin e gabimit („duplicate key value violates unique constraint …“). Bëje vetëm si opsion të fundit. Më të dobishme janë kodet e strukturuara (SQLSTATE, Error Number). Fatkeqësisht jo të gjithë driver-ët japin të gjitha informacionet në të njëjtën mënyrë. Prandaj planifiko të dyja: kodin dhe tekstin, plus opsionalisht „emrin e constraint-it nga teksti“, por pa varësi të fortë.

    Këshilla për debugging: Si të gjesh shpejt rreshtin e prishur

    Bëni batch-in të riprodhueshëm

    Nëse një import dështojnë rastësisht, ke nevojë për riprodhueshmëri. Ruaj për çdo batch një skedar diagnostikimi të vogël ose një hyrje logu që përmban:

    • Numrin e batch-it dhe kohën
    • ArraySize dhe modalitetin e transaksionit
    • listën e çelësave funksionalë (p.sh. ID-të e jashtme) në batch

    Kjo shpesh mjafton për të nisur më pas një mini-import të synuar vetëm për këto ID.

    Bëni SQL-në finale të dukshme (por pa rrjedhje të të dhënave)

    Në debugging dëshiron të dish: A është SQL e saktë? A janë parametrat në rregull? FireDAC ofron monitoring/tracing përmes komponentëve FDMoni dhe logging të driver-ëve. Në ambiente afër-prodhimit është e rëndësishme:

    • Aktivizoni tracing-un vetëm përkohësisht dhe të synuar (performanca dhe mbrojtja e të dhënave).
    • Regjistroni vlerat e parametrave vetëm në një ambient të sigurt ose të maskuara në log.
    • Për të dhëna personale: në log vetëm çelësa teknikë (ID-të) dhe asnjë përmbajtje në tekst të qartë.

    Nëse teston me ndarje (split): përcakto kriteret e ndërprerjes

    Në binary split nuk dëshiron të ndash pa fund. Vendos një kufi të poshtëm, p.sh. „nën 20 rreshta kalo në modalitet të vetëm“. Dhe vendos një limit se sa gabime toleron gjithsej para se të ndërpresësh importin (p.sh. në problemet sistematike të mapping-ut). Përndryshe do të përfundosh me lista gabimesh pa fund dhe bllokon përpunimin pasues.

    Kur vërtet ia vlen përpjekja (dhe kur jo)

    Array DML me error-handling për çdo rresht ia vlen veçanërisht kur:

    • Shumë rreshta përpunohen (mijëra deri miliona).
    • Pak rreshta me gabime janë, por prapë dëshiron që procesi të vazhdojë.
    • Importi duhet të funksionojë në mënyrë të qëndrueshme në operacion (p.sh. përpunim natën, shërbim pa UI).
    • Duhet të rikthesh një listë gabimesh tek departamenti përkatës/burimi (me referencë rreshti).

    Nuk ia vlen aq shumë kur:

    • shkruan vetëm disa dhjetëra rreshta (insert-e individuale janë të pranueshme),
    • cilësia e të dhënave është aq e dobët sa 30–50% e rreshtave dështojnë (atëherë një strategji Staging është më e përshtatshme),
    • përdor tashmë një procedurë të ngarkimit bulk native të DB-së (p.sh. COPY në PostgreSQL, BCP/BULK INSERT në SQL Server) – në atë rast Array DML nuk është mjeti i duhur.

    Arkitekturë alternative: Tabelë Staging në vend të „Direkt në destinacion“

    Nëse përballesh rregullisht me cilësi të përziera të të dhënave, një „insert direkt në tabelën e destinacionit“ shpesh është vendim i gabuar. Një tabelë Staging (fazë paraprake) është një tabelë ku së pari ruan të dhënat në mënyrë teknike korrekte (nëse është e nevojshme me tipe fleksibël), dhe vetëm më pas i validon dhe i transferon në tabelën e destinacionit.

    Përparësitë në operim:

    • Rekordet e gabuar mbeten të ruajtura në mënyrë të gjurmueshme (përfshirë të dhënat e papërpunuara).
    • Validimin mund ta kryesh në mënyrë të ndarë dhe të përsëritshme.
    • Ndani pranimin nga ndërfaqjet nga përpunimi funksional.

    Array DML është shpesh rruga e shpejtë në tabelën Staging, ndërsa transferimi në tabelën e destinacionit bëhet si SQL i bazuar në set (ose si procedurë e ruajtur). Kjo zhvendos trajtimin e gabimeve më shumë në anën e DB-së, gjë që sipas organizimit (rollet DBA, Deployment) mund të jetë e përshtatshme ose e padëshirueshme.

    Operimi dhe Administrimi: Çfarë duhet të dinë IT-Leads dhe Admins

    Monitoring: Shkalla e gabimeve dhe përpunimi janë metrikat kryesore

    Për operim të qëndrueshëm të një importi bulk, dy metrika kanë më shumë peshë se vetëm „koha e ekzekutimit“:

    • Përpunimi: rreshta për minutë (ose për batch) përfshirë pikut/medianën.
    • Norma e gabimeve: rreshtat e gabuar për çdo ekzekutim, idealisht të grupuara sipas klasave të gabimeve (Unique, FK, NOT NULL, konflikt tipi).

    Nëse sheh rregullisht këto dy vlera, do ta kuptosh herët nëse në burim ka ndryshuar diçka (p.sh. format i ri) ose nëse sistemi i destinacionit (p.sh. constraints të reja) është bërë më i rreptë.

    Bllokime dhe dritare ngarkese

    Bulk-Inserts mund të shkaktojnë bllokime dhe ngarkesë IO. Nëse përdoruesit punojnë paralelisht mbi të njëjtat tabela, duhet të mendosh për nivelet e izolimit, indekset dhe eventualisht particionimin. Në praktikë kjo do të thotë: ose vendosni importet në dritare me ngarkesë të ulët, ose ndërtoni rrjedhën e të dhënave në mënyrë që të koeksistojë me operimin në vazhdim (p.sh. përmes Staging + marrje asinkrone).

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

    • Madhësia e batch-it vendosni (vlera fillestare 500–1.000) dhe rregullojeni në mënyrë të matshme.
    • Transaksion eksplizit: commit për secilin batch si parazgjedhje; „commit në fund“ vetëm me vetëdije.
    • Vendosni lloje parametrash të qëndrueshme, mos detyroni kastime implicite.
    • Mbani kontekstin e gabimit për çdo rekord (ID e jashtme, rreshti burim).
    • Strategjia e gabimeve: së pari batch, pastaj ndarje/fallback, regjistrim për çdo rresht.
    • Logging: kode + tekst, por në përputhje me rregullat e mbrojtjes së të dhënave; regjistroni Batch-ID dhe ID e ekzekutimit.
    • Rifillim: sigurohuni për idempotencë (çelësi/Upsert/Import-ID).

    Përfundim: Array DML është i shpejtë – bëhet i qëndrueshëm përmes procesit dhe strategjisë së gabimeve

    Një FireDAC Bulk-Insert me Array DML është një vegël e fuqishme, për sa kohë nuk bën sikur nuk ka gabime. Në rrjedhat reale të të dhënave gjithmonë ka devijime: duplikate, referenca të mungueshme, vlera datash të prishura. Qasja e pastër është për këtë arsye: Array DML për performancën, e kombinuar me një strategji të kontrolluar izolimi (Split ose Fallback) dhe një listë gabimesh të gjurmueshme për çdo rresht. Me këtë fiton shpejtësi dhe siguri operacionale – dhe kjo është pikërisht ajo që vlen kur importet nuk bëhen vetëm në laborator, por duhet të ekzekutohen në mënyrë të besueshme çdo natë.

    Nëse ju dëshironi të stabilizoni një proces ekzistues importi ose ndërfaqe në Delphi/FireDAC (performancë, transaksione, rifillim, regjistrim), e sqarojmë këtë me kënaqësi në mënyrë të strukturuar në një bisedë teknike:

    Për këtë temë janë të rëndësishme edhe Delphi Bulk Insert dhe Bulk Insert Delphi FireDAC. Artikulli vë në kontekst këto aspekte në mënyrë të kuptueshme dhe tregon çfarë ka rëndësi në punën e përditshme.

    Diskutoni projektin ose nismën e modernizimit me Net-Base.

    Hapi tjetër

    Kur nga një temë lind një projekt real, arkitektura, sistemi ekzistues dhe operimi duhet të vlerësohen së bashku që në fillim.

    Ne nuk mbështesim vetëm në çështje të veçanta, por edhe kur nga fragmente të kodit burimor, temat legacy ose idetë për portale duhet të zhvillohen në një projekt korporativ të qëndrueshëm.

    • Gjendja ekzistuese, imazhi i synuar dhe rreziqet teknike vlerësohen së bashku.
    • REST, qasja në të dhëna, portalet dhe implementimi nuk shtyhen si pasojë e mëvonshme.
    • Ju e shihni herët se cila rrugë është e qëndrueshme ekonomikisht dhe operativisht.

    Ndaje postimin

    Shpërndaj këtë postim drejtpërdrejt

    LinkedIn, X, XING, Facebook, WhatsApp dhe E-Mail janë menjëherë të disponueshme. Për Instagram po përgatisim lidhjen dhe tekstin e shkurtër.

    Postë elektronike

    Instagram hapet në një skedë të re. Linku dhe teksti i shkurtër kopjohen më parë në memorjen e kopjimit.