Het Verschil in Één Alinea
Een view in Snowflake slaat geen data op — het is een opgeslagen query die bij elke SELECT opnieuw wordt uitgevoerd tegen de onderliggende tabellen. Een Dynamic Table slaat het resultaat van een query wél fysiek op en houdt dat automatisch en incrementeel actueel, volgens een door jou ingesteld verversingsdoel: de TARGET_LAG.
Het is in de kern dezelfde afweging als bij incremental load vs full load: bereken je opnieuw bij elke aanvraag, of houd je een vooraf berekend resultaat actueel? Alleen speelt die afweging hier binnen Snowflake zelf, op het niveau van transformatielagen.
In één zin
Views zijn goedkoop in rust, duur bij veel queries op grote datasets. Dynamic Tables zijn duur in rust (ze verversen ook als niemand kijkt), maar goedkoop en snel bij veel queries — met een expliciete knop om latency tegen kosten af te wegen.
Wat is een View in Snowflake?
Een standaard VIEW is niets meer dan een naam voor een query. Er wordt geen data opgeslagen — Snowflake voert de onderliggende SELECT uit op het moment dat iemand de view bevraagt.
-- Standaard view: geen opslag, altijd realtime, altijd herberekend CREATE OR REPLACE VIEW analytics.v_omzet_per_klant AS SELECT k.klant_id, k.naam, SUM(o.bedrag) AS totale_omzet, COUNT(o.order_id) AS aantal_orders FROM raw.klanten k JOIN raw.orders o ON o.klant_id = k.klant_id GROUP BY k.klant_id, k.naam;
Voordelen van views:
- Altijd realtime — er is geen verversingsvertraging, want er wordt niets vooraf berekend.
- Geen extra opslag- of verversingskosten — je betaalt alleen compute op het moment dat iemand de view bevraagt.
- Eenvoud — geen planning, geen onderhoud van een verversingsschema.
Nadelen van views:
- Herhaalde berekening — een zware aggregatie over miljoenen rijen wordt bij élke query opnieuw uitgevoerd, ook als de onderliggende data niet is veranderd.
- Traag bij complexe joins/aggregaties op grote datasets, zeker als meerdere views op elkaar worden gestapeld.
- Onvoorspelbare compute-belasting — populaire dashboards die vaak dezelfde zware view bevragen, stapelen kosten op zonder dat je dat centraal kunt beheersen.
Materialized Views: een tussenweg
Snowflake kent ook MATERIALIZED VIEW: het resultaat wordt wél opgeslagen en Snowflake ververst het automatisch zodra de brondata wijzigt. Klinkt als een Dynamic Table, maar met belangrijke beperkingen:
- Geen joins tussen meerdere tabellen — een materialized view mag maar van één brontabel afhangen.
- Geen
ORDER BY, beperkte aggregatiefuncties en geen subqueries. - Geen configureerbare verversingsfrequentie — Snowflake bepaalt zelf wanneer het ververst.
Voor eenvoudige "filter en aggregeer op één tabel"-scenario's is een materialized view prima. Zodra je moet joinen — en dat is in de praktijk bijna altijd — kom je uit bij een Dynamic Table.
Wat is een Dynamic Table?
Een Dynamic Table is een declaratief pipeline-object: je beschrijft de gewenste eindtoestand met een SELECT-statement — inclusief joins, aggregaties, window-functies — en Snowflake bouwt en onderhoudt automatisch de transformatielogica om het resultaat actueel te houden.
-- Dynamic Table: resultaat wordt opgeslagen en incrementeel ververst CREATE OR REPLACE DYNAMIC TABLE analytics.dt_omzet_per_klant TARGET_LAG = '5 minutes' WAREHOUSE = TRANSFORM_WH AS SELECT k.klant_id, k.naam, SUM(o.bedrag) AS totale_omzet, COUNT(o.order_id) AS aantal_orders FROM raw.klanten k JOIN raw.orders o ON o.klant_id = k.klant_id GROUP BY k.klant_id, k.naam;
De query is bijna identiek aan de view hierboven — het verschil zit in TARGET_LAG en WAREHOUSE. Snowflake plant zelf de verversingen zo in dat de data nooit meer dan 5 minuten achterloopt op de bron, en gebruikt daarvoor incrementele verwerking: alleen de rijen die zijn veranderd worden herberekend, niet de hele tabel.
TARGET_LAG in de praktijk
TARGET_LAG is geen harde garantie maar een doelstelling waar Snowflake naartoe plant. Je kunt ook TARGET_LAG = DOWNSTREAM instellen: dan erft de tabel de lag-eis van de Dynamic Tables die er weer bovenop zijn gebouwd, zodat je maar op één plek in de keten een expliciete eis hoeft te definiëren.
Kettens van Dynamic Tables
Dynamic Tables mogen op elkaar voortbouwen — een silver-laag Dynamic Table gebaseerd op een bronze-laag Dynamic Table, en een gold-laag erbovenop. Snowflake berekent automatisch de benodigde verversingsvolgorde en -frequentie van elke laag, vergelijkbaar met een DAG in Airflow of dbt — maar zonder dat je die orchestratie zelf hoeft te bouwen.
-- Silver: schoongemaakte events CREATE OR REPLACE DYNAMIC TABLE silver.events_clean TARGET_LAG = '10 minutes' WAREHOUSE = TRANSFORM_WH AS SELECT event_id, event_type, user_id, event_ts FROM bronze.events_raw WHERE event_type IS NOT NULL; -- Gold: erft de lag-eis van bovenliggende consumers CREATE OR REPLACE DYNAMIC TABLE gold.events_per_user_dag TARGET_LAG = DOWNSTREAM WAREHOUSE = TRANSFORM_WH AS SELECT user_id, DATE_TRUNC('day', event_ts) AS dag, COUNT(*) AS aantal_events FROM silver.events_clean GROUP BY user_id, DATE_TRUNC('day', event_ts);
Refresh Mode: Incrementeel vs Volledig
Snowflake probeert een Dynamic Table standaard incrementeel te verversen: alleen de gewijzigde rijen in de brontabellen worden verwerkt, net als bij een MERGE in een handmatig gebouwde incremental load-pipeline. Niet elke query leent zich hiervoor — sommige constructies (bepaalde window-functies, non-deterministische functies, sommige outer joins) dwingen een volledige verversing af, waarbij de hele tabel opnieuw wordt berekend.
-- Refresh mode van een Dynamic Table controleren SELECT refresh_mode, refresh_mode_reason FROM TABLE(INFORMATION_SCHEMA.DYNAMIC_TABLE_REFRESH_HISTORY( NAME => 'analytics.dt_omzet_per_klant' ));
Dit is een belangrijk aandachtspunt bij ontwerp: een Dynamic Table die onbedoeld altijd volledig ververst, levert nauwelijks kostenvoordeel op ten opzichte van een geplande batch-job. Controleer refresh_mode_reason als de compute-kosten hoger uitvallen dan verwacht.
Views vs Materialized Views vs Dynamic Tables: Samengevat
| Dimensie | View | Materialized View | Dynamic Table |
|---|---|---|---|
| Opslag van resultaat | Nee — altijd herberekend | Ja | Ja |
| Actualiteit | Altijd realtime | Automatisch, niet configureerbaar | Configureerbaar via TARGET_LAG |
| Joins tussen tabellen | Onbeperkt | Niet toegestaan (één brontabel) | Onbeperkt |
| Kosten in rust | Geen | Laag (incrementele verversing) | Doorlopend — ververst ook zonder queries |
| Kosten bij veel queries | Hoog — elke query herberekent | Laag — leest opgeslagen resultaat | Laag — leest opgeslagen resultaat |
| Kettens/lagen (bronze → silver → gold) | Mogelijk, maar elke laag herberekent bij elke query | Niet praktisch | Native ondersteund, met afgeleide lag per laag |
| Onderhoud | Geen | Minimaal | Warehouse-sizing en TARGET_LAG afstemmen |
Welke Kies Je? Een Praktische Beslisboom
- Data wordt zelden bevraagd, of moet altijd 100% realtime zijn? Kies een gewone view — geen verversingskosten, geen latency-afweging nodig.
- Eenvoudige filter/aggregatie op precies één brontabel, met weinig eisen aan de verversingsfrequentie? Een materialized view volstaat en is het minst complex om te onderhouden.
- Zware aggregatie of join die vaak wordt bevraagd (bijv. een dashboard dat elke minuut ververst) en waarbij een paar minuten latency acceptabel is? Kies een Dynamic Table — de compute-kosten verschuiven van "bij elke query" naar "bij elke verversing", wat bij veel lezers voordeliger is.
- Meerlaagse transformatiepipeline (bronze → silver → gold) die je nu handmatig met taken of dbt orchestreert? Overweeg Dynamic Tables als vervanging — Snowflake regelt de afhankelijkheden en verversingsvolgorde automatisch.
- Onvoorspelbaar of zeldzaam queryverkeer op een grote, dure aggregatie? Blijf bij een view. Een Dynamic Table die ververst zonder ooit bevraagd te worden, is pure verspilling.
Vuistregel
Begin met een view. Migreer naar een Dynamic Table zodra je merkt dat dezelfde zware query herhaaldelijk door meerdere consumers wordt uitgevoerd — dát is het punt waarop vooraf berekenen en incrementeel actueel houden goedkoper wordt dan telkens opnieuw berekenen.
Veelvoorkomende Valkuilen bij Dynamic Tables
Te agressieve TARGET_LAG
Een TARGET_LAG van '1 minute' op een zwaar getransformeerde tabel dwingt Snowflake tot frequente verversingen, ook als de brondata nauwelijks verandert. Stem de lag af op wat de consument daadwerkelijk nodig heeft — een dashboard dat elke 15 minuten wordt bekeken heeft geen lag van 1 minuut nodig.
Onbedoelde volledige verversing
Zoals eerder genoemd: bepaalde queryconstructies forceren een refresh_mode = FULL. Controleer dit bij het ontwerpen van de query — vaak is een kleine herformulering (bijvoorbeeld een deterministische functie in plaats van een non-deterministische) genoeg om incrementele verversing mogelijk te maken.
Verkeerde warehouse-sizing
Elke Dynamic Table draait verversingen op een gekoppeld warehouse. Een te klein warehouse verlengt de verversingstijd (en kan de TARGET_LAG-doelstelling laten missen); een te groot warehouse verspilt compute-budget bij kleine, frequente verversingen. Monitor via DYNAMIC_TABLE_REFRESH_HISTORY en pas de warehouse-grootte aan op basis van de daadwerkelijke verversingsduur.
Dynamic Tables als vervanging voor échte streaming
Een TARGET_LAG van enkele seconden is technisch mogelijk, maar in de praktijk kostbaar en meestal overkill. Voor écht near-realtime verwerking met sub-seconde latency is een streamingplatform zoals Apache Kafka gecombineerd met Snowpipe Streaming vaak een beter passende oplossing dan een Dynamic Table met een extreem lage lag.
Dynamic Tables en dbt
dbt ondersteunt Dynamic Tables als materialisatie op Snowflake: in plaats van materialized='table' of materialized='incremental' gebruik je materialized='dynamic_table' en configureer je target_lag en snowflake_warehouse in de modelconfiguratie.
-- models/omzet_per_klant.sql {{ config( materialized='dynamic_table', target_lag='5 minutes', snowflake_warehouse='TRANSFORM_WH' ) }} SELECT k.klant_id, k.naam, SUM(o.bedrag) AS totale_omzet FROM {{ ref('klanten') }} k JOIN {{ ref('orders') }} o ON o.klant_id = k.klant_id GROUP BY k.klant_id, k.naam;
Dit combineert het beste van beide werelden: dbt's versiebeheer, testing en documentatie, met de automatische incrementele verversing van Snowflake zelf — zonder dat je zelf is_incremental()-logica hoeft te schrijven zoals bij een klassiek incremental model.
Conclusie
Views, materialized views en Dynamic Tables zijn geen concurrenten maar drie punten op dezelfde schaal: van "altijd opnieuw berekenen, nooit vooraf opslaan" tot "automatisch en incrementeel actueel houden volgens een expliciete lag-doelstelling". De juiste keuze hangt af van hoe vaak data wordt bevraagd, hoe zwaar de transformatie is en hoeveel latency acceptabel is.
Dynamic Tables zijn met name waardevol zodra je meerdere transformatielagen handmatig orchestreert met taken of externe tools — Snowflake neemt die afhankelijkheidslogica dan grotendeels over, met minder onderhoud als resultaat.
Twijfel je of jouw Snowflake-views omgezet moeten worden naar Dynamic Tables, of wil je een bestaande transformatiepipeline herontwerpen? Neem contact op — ik denk graag mee. Zie ook Incremental Load vs Full Load voor de bredere afweging rond verversingsstrategieën en Snowflake DCM Projects voor hoe je deze objecten declaratief beheert.
Veelgestelde vragen
Wat is een Dynamic Table in Snowflake?
Een Dynamic Table is een Snowflake-object waarin je een query declareert en Snowflake automatisch en incrementeel de resultaten actueel houdt, op basis van een ingesteld TARGET_LAG. Het resultaat wordt fysiek opgeslagen in plaats van bij elke query herberekend.
Wat is het verschil tussen een Dynamic Table en een gewone view?
Een gewone view slaat geen data op — de query wordt bij elke SELECT opnieuw uitgevoerd. Een Dynamic Table slaat het resultaat op en houdt dat automatisch actueel volgens TARGET_LAG, waardoor query's sneller zijn maar de data nooit 100% realtime is.
Wat is het verschil tussen een Dynamic Table en een materialized view?
Een materialized view ondersteunt geen joins tussen meerdere brontabellen en heeft geen configureerbare verversingsfrequentie. Een Dynamic Table ondersteunt joins, aggregaties en kettens van Dynamic Tables, met een expliciet TARGET_LAG per object.
Wat is TARGET_LAG?
TARGET_LAG bepaalt hoe ver de data in een Dynamic Table maximaal mag achterlopen op de bron. Snowflake plant automatisch verversingen om aan deze doelstelling te voldoen, en berekent bij een keten van Dynamic Tables de benodigde frequentie van elke laag terug.
Kosten Dynamic Tables meer dan views?
Dynamic Tables verbruiken compute bij elke verversing, ook zonder queries. Views kosten alleen compute op het moment dat iemand ze bevraagt. Bij weinig queries op een grote dataset kan een view daarom goedkoper zijn; bij veel queries is een Dynamic Table vaak voordeliger.