Gegenereerde kolommen vs triggers in PostgreSQL: wat te gebruiken?
Gegenereerde kolommen vs triggers in PostgreSQL: kies de juiste aanpak voor totalen, statussen en genormaliseerde waarden met duidelijke afwegingen qua snelheid en debuggemak.

Welk probleem lossen we met afgeleide velden op?
Een afgeleid veld is een waarde die je opslaat of toont omdat je die uit andere data kunt berekenen. In plaats van dezelfde berekening in elke query en op elk scherm te herhalen, definieer je de regel één keer en hergebruik je die.
Veelvoorkomende voorbeelden zijn makkelijk voor te stellen:
order_totalis gelijk aan de som van orderregels, minus kortingen, plus belasting- een status zoals "paid" of "overdue" gebaseerd op datums en betalingsrecords
- een genormaliseerde waarde zoals een lowercased e-mail, een getrimde telefoonnummer, of een zoekvriendelijke versie van een naam
Teams gebruiken afgeleide velden omdat lezen eenvoudiger en consistenter wordt. Een rapport kan order_total direct selecteren. Support kan filteren op status zonder ingewikkelde logica te kopiëren. Eén gedeelde regel vermindert ook kleine verschillen tussen services, dashboards en achtergrondjobs.
De risico’s zijn echter reëel. Het grootste is verouderde data: de inputs veranderen, maar de afgeleide waarde niet. Een andere is verborgen logica: de regel leeft in een trigger, een functie of een oude migratie en niemand herinnert zich dat die er is. Een derde is duplicatie: je krijgt bijna-gelijke regels op meerdere plekken en ze drijven met de tijd uit elkaar.
Daarom maakt de keuze tussen gegenereerde kolommen en triggers in PostgreSQL uit. Je kiest niet alleen hoe je een waarde berekent. Je kiest waar de regel woont, wat het kost bij writes en hoe makkelijk het is om een verkeerd getal terug te leiden naar de oorzaak.
De rest van dit artikel kijkt naar drie praktische invalshoeken: onderhoudbaarheid (kunnen mensen het begrijpen en aanpassen), query-snelheid (lezen, schrijven, indexen) en debugging (hoe vind je waarom een waarde fout is).
Tegen elkaar: eenvoudige definities
Als mensen gegenereerde kolommen en triggers in PostgreSQL vergelijken, kiezen ze in feite waar een afgeleide waarde moet wonen: in de tabeldefinitie, of in procedurele logica die draait wanneer data verandert.
Gegenereerde kolommen
Een gegenereerde kolom is een echte tabelkolom waarvan de waarde wordt berekend uit andere kolommen in dezelfde rij. In PostgreSQL zijn gegenereerde kolommen opgeslagen (de database bewaart het berekende resultaat op schijf) en worden ze automatisch bijgewerkt wanneer de gerefereerde kolommen veranderen.
Een gegenereerde kolom gedraagt zich als een normale kolom voor query’s en indexering, maar je schrijft er niet direct naar. Als je een berekende waarde nodig hebt die niet wordt opgeslagen, gebruikt PostgreSQL meestal een view (of een query-expressie) in plaats van een gegenereerde kolom.
Triggers
Een trigger is logica die draait bij events zoals INSERT, UPDATE of DELETE. Triggers kunnen voor of na de wijziging draaien, en ze kunnen per rij of per statement draaien.
Omdat triggers code zijn, kunnen ze meer dan eenvoudige rekenkunde doen. Ze kunnen andere kolommen bijwerken, naar andere tabellen schrijven, aangepaste regels afdwingen en reageren op wijzigingen over meerdere rijen.
Een handige manier om het verschil te onthouden:
- Gegenereerde kolommen passen bij voorspelbare, rij-niveau berekeningen (totalen, genormaliseerde tekst, simpele flags) die altijd bij de huidige rij moeten passen.
- Triggers passen bij regels die timing, bijwerkingen of cross-row en cross-table logica hebben (statustransities, auditlogs, voorraadaanpassingen).
Nog een noot over constraints: ingebouwde constraints (NOT NULL, CHECK, UNIQUE, foreign keys) zijn duidelijk en declaratief, maar beperkt. Bijvoorbeeld, een CHECK-constraint kan niet afhankelijk zijn van andere rijen via een subquery. Wanneer een regel meer nodig heeft dan de huidige rij, kom je meestal uit bij triggers of een herontwerp.
Als je bouwt met een visuele tool zoals AppMaster, vertaalt dit verschil zich netjes naar “data model formula”-achtige regels versus “business process”-regels die draaien wanneer records veranderen.
Onderhoudbaarheid: wat blijft leesbaar over tijd?
Het grootste verschil in onderhoudbaarheid is waar de regel woont.
Een gegenereerde kolom houdt de logica dicht bij de data-definitie. Wanneer iemand het tabelschema opent, ziet diegene de expressie die de waarde produceert.
Bij triggers verhuist de regel naar een trigger-functie. Je moet ook weten welke tabellen en events hem aanroepen. Maanden later betekent “leesbaarheid” vaak: kan iemand de regel begrijpen zonder door de database te moeten speuren? Gegenereerde kolommen winnen hier meestal omdat de definitie op één plek zichtbaar is en minder bewegende delen heeft.
Triggers kunnen nog steeds schoon zijn als je de functie klein en gefocust houdt. Het probleem begint wanneer een triggerfunctie een verzamelbak wordt voor ongerelateerde regels. Het werkt misschien, maar het wordt moeilijk om over na te denken en riskant om aan te passen.
Wijzigingen zijn een ander drukpunt. Bij gegenereerde kolommen zijn updates meestal een migratie die één expressie wijzigt. Dat is eenvoudig te reviewen en terug te draaien. Triggers vereisen vaak gecoördineerde wijzigingen in de functietekst en triggerdefinitie, plus extra stappen voor backfills en safety checks.
Om regels op termijn vindbaar te houden, helpen een paar gewoonten:
- Geef kolommen, triggers en functies namen die naar de businessregel verwijzen.
- Voeg korte opmerkingen toe die intentie uitleggen, niet alleen de rekenregels.
- Houd triggerfuncties klein (één regel, één tabel).
- Bewaar migraties in versiebeheer en vereis reviews.
- Maak periodiek een lijst van alle triggers in het schema en verwijder wat je niet meer nodig hebt.
Hetzelfde idee geldt in AppMaster: geef voorkeur aan regels die je snel kunt zien en auditen, en houd “verborgen” write-time logica tot een minimum.
Query-snelheid: wat verandert voor lezen, schrijven en indexen?
De prestatievraag is in wezen: wil je de kosten betalen bij lezen of bij schrijven?
Een gegenereerde kolom wordt berekend wanneer de rij wordt weggeschreven en vervolgens opgeslagen. Lezen is snel omdat de waarde al aanwezig is. De afweging is dat elke INSERT en elke UPDATE die de inputs raakt, ook de gegenereerde waarde moet berekenen.
Een trigger-gebaseerde aanpak slaat meestal de afgeleide waarde op in een normale kolom en houdt die bijgewerkt met een trigger. Lezen is dan ook snel, maar schrijven kan trager en minder voorspelbaar zijn. Triggers voegen extra werk per rij toe, en die overhead wordt merkbaar tijdens bulk-updates.
Indexering is waar opgeslagen afgeleide waarden het meest tellen. Als je vaak filtert of sorteert op een afgeleid veld (een genormaliseerde e-mail, een total, een statuscode), kan een index een trage scan veranderen in een snelle lookup. Met gegenereerde kolommen kun je de gegenereerde waarde direct indexeren. Met triggers kun je ook de onderhouden kolom indexeren, maar je vertrouwt erop dat de trigger hem correct bijhoudt.
Als je de waarde in de query rekent (bijvoorbeeld in een WHERE-clausule), heb je mogelijk een expression index nodig om te voorkomen dat hij voor veel rijen opnieuw wordt berekend.
Bulkimports en grote updates zijn veelvoorkomende knelpunten:
- Gegenereerde kolommen voegen een consistente rekenkost toe aan elke aangeraakte rij.
- Triggers voegen rekenkost plus trigger-overhead toe, en slecht geschreven logica kan die kost vermenigvuldigen.
- Grote updates kunnen het triggerwerk de bottleneck maken.
Een praktische keuze is kijken naar echte hotspots. Als de tabel leesintensief is en het afgeleide veld veel gebruikt wordt in filters, winnen opgeslagen waarden (gegenereerd of door trigger onderhouden) plus een index meestal. Als de tabel schrijfintensief is (events, logs), wees dan voorzichtig met per-rij werk tenzij het echt nodig is.
Debugging: de bron van verkeerde waarden vinden
Als een afgeleid veld verkeerd is, begin dan met het reproduceerbaar maken van de bug. Leg de exacte rijstaat vast die de verkeerde waarde produceerde en voer daarna dezelfde INSERT of UPDATE opnieuw uit in een schone transactie zodat je geen bijwerkingen najaagt.
Een snelle manier om het te verkleinen is: kwam de waarde uit een deterministische expressie, of uit write-time logica?
Gegenereerde kolommen falen meestal op consistente manieren. Als de expressie fout is, is die voor dezelfde inputs altijd fout. Veelvoorkomende verrassingen zijn NULL-handling (één NULL kan de hele berekening NULL maken), impliciete casts (tekst naar numeric), en randgevallen zoals deling door nul. Als resultaten tussen omgevingen verschillen, kijk dan naar verschillen in collatie, extensies of schemawijzigingen die de expressie veranderden.
Triggers falen op rommeliger manieren omdat ze van timing en context afhangen. Een trigger vuurt mogelijk niet wanneer je denkt (verkeerd event, verkeerde tabel, ontbrekende WHEN-clausule). Hij kan meerdere keren vuren via triggerketens. Bugs kunnen ook komen door sessie-instellingen, search_path, of het lezen van andere tabellen die tussen omgevingen verschillen.
Wanneer een afgeleide waarde verkeerd lijkt, is deze checklist meestal genoeg om de oorzaak te vinden:
- Reproduceer met een minimale INSERT/UPDATE en de kleinste voorbeeldrij.
- Selecteer de ruwe invoerkolommen naast de afgeleide kolom om de inputs te bevestigen.
- Voor gegenereerde kolommen: voer de expressie in een SELECT uit en vergelijk.
- Voor triggers: voeg tijdelijk RAISE LOG-notities toe of schrijf naar een debugtabel.
- Vergelijk schema- en triggerdefinities tussen omgevingen.
Kleine testdatasets met bekende uitkomsten verminderen verrassingen. Maak bijvoorbeeld twee orders: één met NULL-discount en één met discount 0, en bevestig dat totals zich zoals verwacht gedragen. Doe hetzelfde voor statustransities en verifieer dat ze alleen plaatsvinden bij de bedoelde updates.
Hoe kies je: een beslisroute
De beste keuze wordt meestal duidelijk als je een paar praktische vragen beantwoordt.
Stap 1–3: correctheid eerst, daarna workload
Werk deze volgorde af:
- Moet de waarde altijd exact overeenkomen met andere kolommen, zonder uitzonderingen? Zo ja, handhaaf het in de database in plaats van het in de app te zetten en hopen dat het correct blijft.
- Is de formule deterministisch en gebaseerd alleen op kolommen in dezelfde rij (bijv.
lower(email)ofprice * quantity)? Zo ja, is een gegenereerde kolom meestal de schoonste optie. - Lees je deze waarde vooral (filteren, sorteren, rapportage) of schrijf je er veel naar (veel inserts/updates)? Gegenereerde kolommen verschuiven kosten naar writes, dus schrijfintensieve tabellen zullen het eerder voelen.
Als de regel afhankelijk is van andere rijen, andere tabellen of tijdgevoelige logica (bijv. “zet status op overdue als na 7 dagen geen betaling”), is een trigger vaak een betere keuze omdat die rijkere logica kan uitvoeren.
Stap 4–6: indexeren, testen en eenvoudig houden
Bepaal nu hoe de waarde wordt gebruikt en geverifieerd:
- Ga je er vaak op filteren of sorteren? Plan dan een index en bevestig dat je aanpak dat netjes ondersteunt.
- Hoe ga je testen en wijzigingen observeren? Gegenereerde kolommen zijn makkelijker te begrijpen omdat de regel in één expressie staat. Triggers hebben gerichte tests en duidelijke logging nodig omdat de waarde “aan de zijkant” verandert.
- Kies de eenvoudigste optie die aan de eisen voldoet. Als een gegenereerde kolom werkt, is die meestal makkelijker te onderhouden. Heb je cross-row regels, multi-step statuswijzigingen of bijwerkingen nodig, accepteer dan de trigger, maar houd hem klein en goed benoemd.
Een goede vuistregel: als je de regel in één zin uit kunt leggen en hij gebruikt alleen de huidige rij, begin met een gegenereerde kolom. Als je een workflow beschrijft, zit je waarschijnlijk in trigger-terrein.
Gegenereerde kolommen voor totalen en genormaliseerde waarden
Gegenereerde kolommen werken goed wanneer de waarde volledig afgeleid is van andere kolommen in dezelfde rij en de regel stabiel is. Hier voelen ze het meest simpel: de formule staat in de tabeldefinitie en PostgreSQL houdt hem consistent.
Typische voorbeelden zijn genormaliseerde waarden (zoals een lowercased, getrimde sleutel voor lookups) en eenvoudige totalen (zoals subtotal + tax - discount). Bijvoorbeeld, een orders-tabel kan subtotal, tax en discount opslaan en total als een gegenereerde kolom exposen zodat elke query hetzelfde getal ziet zonder op applicatiecode te vertrouwen.
Bij het schrijven van de expressie: houd het saai en defensief:
- Handel NULLs af met
COALESCEzodat totalen niet onverwacht NULL worden. - Cast bewust om te voorkomen dat je per ongeluk integers en numerics mengt.
- Rond op één plek af en documenteer de afrondingsregel in de expressie.
- Maak timezone- en tekstregels expliciet (lowercasing, trimming, spaties vervangen).
- Geef de voorkeur aan een paar helperkolommen boven één gigantische formule.
Indexeren helpt alleen als je er echt op filtert of joinet. Een index op een gegenereerde total is vaak weggegooid werk als je nooit op total zoekt. Een index op een genormaliseerde sleutel zoals email_normalized is vaak wél de moeite waard.
Schemawijzigingen zijn belangrijk omdat gegenereerde expressies afhankelijk zijn van andere kolommen. Het hernoemen van een kolom of het veranderen van een type kan de expressie breken — wat een goede faalmodus is. Je ontdekt dat tijdens een migratie in plaats van stilletjes foute data te schrijven.
Als de formule te veel begint uit te dijen (veel CASE-takken, veel businessregels), zie dat als een signaal. Splits onderdelen in aparte kolommen of stap over van aanpak zodat de regel leesbaar en testbaar blijft. In AppMaster werken gegenereerde kolommen het beste wanneer de regel makkelijk te zien en uit te leggen is in één regel.
Triggers voor statussen en cross-row regels
Triggers zijn vaak het juiste gereedschap wanneer een veld afhankelijk is van meer dan de huidige rij. Statusvelden zijn een veelvoorkomend voorbeeld: een order wordt "paid" pas als er minstens één succesvolle betaling bestaat, of een ticket wordt "resolved" alleen wanneer elke taak klaar is. Zulke regels kruisen rijen of tabellen, wat gegenereerde kolommen niet kunnen lezen.
Een goede trigger is klein en saai. Behandel hem als een vangrail, niet als een tweede applicatie.
Houd triggers voorspelbaar
Verborgen writes maken triggers moeilijk om mee te leven. Een eenvoudige conventie helpt andere ontwikkelaars te zien wat er gebeurt:
- Eén trigger voor één doel (statusupdates, niet totals plus audit plus notificaties).
- Duidelijke namen (bijv.
trg_orders_set_status_on_payment). - Consistente timing: gebruik BEFORE om binnenkomende data te corrigeren, AFTER om te reageren op opgeslagen rijen.
- Houd de logica in één functie, kort genoeg om in één keer te lezen.
Een realistische flow: payments wordt geüpdatet naar succeeded. Een AFTER UPDATE-trigger op payments werkt orders.status bij naar paid als de order minstens één geslaagde betaling heeft en geen openstaand saldo.
Randgevallen om op te plannen
Triggers gedragen zich anders bij bulkwijzigingen. Bepaal voordat je commit hoe je backfills en reruns aanpakt. Een eenmalige SQL-job om status voor oude data te herberekenen is vaak duidelijker dan triggers rij-voor-rij laten vuuren. Het helpt ook om een veilige “reprocessing”-route te definiëren, zoals een stored procedure die status voor één order herberekent. Houd idempotentie in gedachten zodat het opnieuw uitvoeren van dezelfde update staten niet verkeerd om zet.
Controleer tenslotte of een constraint of applicatielogica beter past. Voor eenvoudige toegestane waarden zijn constraints duidelijker. In tools zoals AppMaster zijn veel workflows ook makkelijker zichtbaar in de business logic-laag, terwijl de database-trigger dient als een nauwe veiligheidsnet.
Veelgemaakte fouten en valkuilen
Veel pijn rond afgeleide velden is zelftoegebracht. De grootste valkuil is het standaard kiezen van het complexere gereedschap. Begin met de vraag: kan dit worden uitgedrukt als een pure expressie op dezelfde rij? Zo ja, is een gegenereerde kolom vaak de rustigere keuze.
Een andere veelgemaakte fout is triggers langzaam laten groeien tot een tweede applicatielaag. Het begint met “even de status zetten” en groeit naar prijsregels, uitzonderingen en speciale gevallen. Zonder tests kunnen kleine aanpassingen oud gedrag breken op manieren die moeilijk te merken zijn.
Valkuilen die steeds terugkomen:
- Een trigger gebruiken voor een per-rij waarde wanneer een gegenereerde kolom duidelijker en zelfdocumenterend zou zijn.
- Een opgeslagen totaal bijwerken in één codepad (checkout) maar vergeten een ander pad (admin edits, imports, backfills).
- Concurrency negeren: twee transacties updaten dezelfde orderregels en je trigger overschrijft of past een wijziging dubbel toe.
- Elke afgeleide waarde “voor het geval” indexeren, vooral waarden die vaak veranderen.
- Opslaan wat je ook bij leestijd kunt berekenen, zoals een genormaliseerde string die zelden wordt doorzocht.
Een klein voorbeeld: je slaat order_total_cents op en laat support orderregels aanpassen. Als de supporttool de regels updatet maar niet het totaal aanraakt, raakt het totaal verouderd. Als je later een trigger toevoegt, moet je nog steeds omgaan met historische rijen en randgevallen zoals gedeeltelijke refunds.
Als je bouwt met een visuele tool zoals AppMaster, geldt dezelfde regel: houd bedrijfsregels leesbaar op één plek. Verspreid updates van afgeleide waarden niet over meerdere flows.
Snelle checks voordat je commit
Voordat je kiest tussen gegenereerde kolommen en triggers in PostgreSQL, doe een korte stresstest van de regel die je wilt opslaan.
Vraag eerst waarvan de regel afhankelijk is. Als het kan worden berekend uit kolommen in dezelfde rij (een genormaliseerd telefoonnummer, een lowercased e-mail, line_total = qty * price), is een gegenereerde kolom meestal makkelijker omdat de logica naast de tabeldefinitie staat.
Als de regel afhankelijk is van andere rijen of tabellen (een orderstatus die verandert wanneer de laatste betaling arriveert, een accountflag op basis van recente activiteit), dan zit je in trigger-terrein of je berekent het bij het lezen.
Een korte checklist:
- Kan de waarde alleen uit de huidige rij worden afgeleid, zonder lookups?
- Moet je er vaak op filteren of sorteren?
- Moet je ooit historische data herberekenen nadat de regel verandert?
- Kan een ontwikkelaar de definitie vinden en in minder dan 2 minuten uitleggen?
- Heb je een kleine set voorbeeldrijen die bewijst dat de regel werkt?
Denk dan aan operatiezaken. Bulkupdates, imports en backfills zijn waar triggers mensen verrassen. Triggers vuren per rij tenzij je zorgvuldig ontwerpt, en fouten tonen zich als trage loads, lock-wachten of half-bijgewerkte afgeleide waarden.
Een praktische test is eenvoudig: laad 10.000 rijen in een stagingtabel, voer je gebruikelijke import uit en verifieer wat wordt berekend. Update daarna een belangrijke invoerkolom en bevestig dat de afgeleide waarde correct blijft.
Als je een app bouwt met AppMaster, geldt hetzelfde: zet simpele rij-gebaseerde regels in de database als gegenereerde kolommen en houd multi-step, cross-table statuswijzigingen op één plek waar je ze herhaaldelijk kunt testen.
Een realistisch voorbeeld: orders, totalen en een statusveld
Stel je een simpele winkel voor. Je hebt een orders-tabel met items_subtotal, tax, total en een payment_status. Het doel is dat iedereen snel kan antwoorden: waarom is deze order nog onbetaald?
Optie A: gegenereerde kolommen voor totalen, status handmatig opgeslagen
Voor geldberekeningen die alleen afhankelijk zijn van waarden in dezelfde rij passen gegenereerde kolommen goed. Je kunt items_subtotal en tax als reguliere kolommen opslaan en total definiëren als een gegenereerde kolom zoals items_subtotal + tax. Dat houdt de regel zichtbaar op de tabel en voorkomt verborgen write-time logica.
Voor payment_status kun je het als een normale kolom houden die je app zet wanneer een betaling wordt aangemaakt. Dat is minder automatisch, maar eenvoudig te beredeneren als je later de rij leest.
Optie B: triggers voor statuswijzigingen gedreven door betalingen
Voeg nu een payments-tabel toe. Status hangt niet langer alleen van één orderrij af. Hij is afhankelijk van gerelateerde rijen zoals succesvolle betalingen, refunds en chargebacks. Een trigger op payments kan orders.payment_status bijwerken wanneer een betaling verandert.
Kies je deze route, plan dan een backfill: een eenmalig script dat payment_status voor bestaande orders herberekent, en een herhaalbare job die je opnieuw kunt draaien als er een bug in sluipt.
Als support onderzoekt waarom een order onbetaald is, stuurt Optie A ze meestal naar de app en de audittrail. Optie B stuurt ze ook naar databasedeel: vuurde de trigger, faalde hij, of sloeg hij over omdat een conditie niet werd gehaald?
Na release let je op een paar signalen:
- trage updates op
payments(triggers voegen werk toe aan writes) - onverwachte updates aan
orders(status flipt vaker dan verwacht) - rijen waar
totalgoed lijkt maar status fout is (logica verdeeld over plekken) - deadlocks of lock waits tijdens piekbetalingsverkeer
Volgende stappen: kies de eenvoudigste aanpak en houd regels zichtbaar
Schrijf de regel in gewone taal voordat je SQL aanraakt. "Order total equals sum of line items minus discount" is duidelijk. "Status is paid when paid_at is set and balance is zero" is duidelijk. Als je het niet in één of twee zinnen kunt uitleggen, hoort het waarschijnlijk op een plek waar het beoordeeld en getest kan worden, niet weggestopt in een snelle database-hack.
Als je vastzit, behandel het als een experiment. Bouw een kleine kopie van de tabel, laad een klein dataset dat op echt leven lijkt en probeer beide benaderingen. Vergelijk wat je werkelijk belangrijk vindt: leesqueries, schrijfsnelheid, indexgebruik en hoe makkelijk het later te begrijpen is.
Een compacte checklist voor beslissen:
- Prototypeer beide opties en bekijk queryplannen voor veelvoorkomende reads.
- Voer een schrijfintensieve test uit (imports, updates) om de kosten van het up-to-date houden van waarden te zien.
- Voeg een klein testscript toe dat backfills, NULLs, afronding en randgevallen dekt.
- Bepaal wie de logica op lange termijn bezit (DBA, backend, product) en documenteer die keuze.
Als je een intern hulpmiddel of portal bouwt, is zichtbaarheid net zo belangrijk als correctheid. Met AppMaster (appmaster.io) houden teams vaak eenvoudige rij-gebaseerde regels dicht bij het datamodel en zetten multi-step veranderingen in een Business Process, zodat de logica tijdens reviews leesbaar blijft.
Eén laatste ding dat later uren bespaart: documenteer waar de waarheid woont (tabel, trigger of applicatielogica) en hoe je veilig opnieuw kunt berekenen als je een backfill nodig hebt.
FAQ
Gebruik een afgeleid veld wanneer veel queries en schermen dezelfde waarde nodig hebben en je één gedeelde definitie wilt. Het is vooral nuttig voor waarden waarop je vaak filtert, sorteert of die je toont, zoals genormaliseerde sleutels, eenvoudige totalen of een consistente flag.
Kies een gegenereerde kolom wanneer de waarde puur een functie is van andere kolommen in dezelfde rij en altijd exact mee moet veranderen. Het houdt de regel zichtbaar in de tabelstructuur en voorkomt verborgen write-time codepaden.
Gebruik een trigger wanneer de regel afhankelijk is van andere rijen of tabellen, of wanneer je bijwerkingen nodig hebt zoals het bijwerken van een gerelateerd record of het wegschrijven van een audit-entry. Triggers passen ook goed bij workflow-achtige overgangen waar timing en context belangrijk zijn.
Gegenereerde kolommen mogen alleen kolommen uit dezelfde rij refereren, dus ze kunnen geen betalingen, orderregels of andere gerelateerde records opzoeken. Als je “total” child rows moet optellen, reken je dat meestal in een query, houd je het bij met triggers, of ontwerp je het schema zodanig dat de benodigde invoer op dezelfde rij staat.
Een gegenereerde kolom slaat de berekende waarde op tijdens het schrijven, dus lezen is snel en indexeren is eenvoudig, maar inserts en updates betalen de rekentijd. Triggers verplaatsen ook werk naar writes en kunnen trager en minder voorspelbaar zijn als de logica complex is of in ketens vuurt tijdens bulkupdates.
Indexeer wanneer je vaak filtert, joinet of sorteert op die afgeleide waarde en het resultaat wezenlijk verkleint, zoals een genormaliseerde e-mail of een statuscode. Als je de waarde alleen toont en er nooit op zoekt, voegt een index meestal schrijf-overhead toe zonder veel voordeel.
Gegenereerde kolommen zijn meestal makkelijker te onderhouden omdat de logica in de tabeldefinitie staat waar mensen van nature kijken. Triggers blijven onderhoudbaar als elke trigger een smal doel heeft, een duidelijke naam en een kleine functie die makkelijk te reviewen is.
Voor gegenereerde kolommen zijn de meest voorkomende problemen NULL-handling, typeconversies en afrondingsregels die onverwacht werken. Voor triggers ontstaan problemen vaak doordat de trigger niet vuurt, meerdere keren vuurt, in een andere volgorde loopt, of afhankelijk is van sessie-instellingen die tussen omgevingen verschillen.
Reproduceer precies de insert of update die de foute waarde produceerde en vergelijk daarna de invoerkolommen naast de afgeleide waarde. Voor een gegenereerde kolom voer je dezelfde expressie in een SELECT uit om te bevestigen dat het resultaat overeenkomt; voor een trigger inspecteer je de trigger- en functie-definities en voeg je minimale logging toe om te bevestigen wanneer en hoe hij draait.
Als je de regel in één zin kunt uitleggen en ze alleen de huidige rij gebruikt, is een gegenereerde kolom een sterke default. Als je een workflow beschrijft of gerelateerde records referent, gebruik dan een trigger of bereken het bij het lezen, en houd de logica op één plek die je kunt testen; in AppMaster betekent dat vaak eenvoudige rijregels dicht bij het datamodel en cross-table workflows in een Business Process.


