← Terug naar Downloads DataPartner365 — datapartner365.nl
DataPartner365 · Handleiding · 2026

Datawarehouse Opzetten

Van architectuurkeuze tot productie: een complete handleiding voor het ontwerpen en bouwen van een schaalbaar, performant datawarehouse op moderne cloud platforms.

SQL Azure Snowflake Databricks ETL/ELT Gratis
1

Architectuurkeuze

Cloud vs on-prem, platforms vergelijken, kosten inschatten

De keuze voor je datawarehouse platform bepaalt je kosten, schaalbaarheid en onderhoudslasten voor jaren. Cloud-native platforms zijn voor de meeste organisaties de beste keuze.

PlatformTypeSterk inKosten model
SnowflakeCloud SaaSMulti-cloud, eenvoud, data sharingPay-per-second compute
Azure SynapseCloud PaaSAzure integratie, hybrideDWU of serverless
DatabricksCloud PaaSBig data, ML, streamingDBU per cluster
BigQueryCloud SaaSGoogle ecosystem, serverlessPay-per-query of flat rate
SQL ServerOn-prem/CloudBestaande MS licentiesLicentie + hardware

Beslissingscriteria

  • Datavolume: < 100 GB → eenvoud prioriteit; > 1 TB → schaalbaarheid prioriteit
  • Concurrente gebruikers: Snowflake en Synapse schalen goed horizontaal
  • Bestaande cloud: Azure-shop → Synapse/Databricks; Google-shop → BigQuery
  • ML/AI workloads: Databricks is de sterkste keuze
  • Compliance: Data residency vereisten kunnen de cloud-keuze beperken
ℹ️ Voor startende organisaties: begin met Snowflake (eenvoudig te beheren, uitstekende documentatie) of Azure Synapse serverless (geen kosten voor opslag die je al in ADLS hebt).
2

Lagenmodel (Bronze / Silver / Gold)

Medallion architecture, databewegingen, verantwoordelijkheden

Het Medallion-architectuurpatroon organiseert data in drie lagen met toenemende kwaliteit en transformatiegraad. Dit is de industriestandaard voor moderne datawarehouses.

LaagAliasInhoudWie schrijft
BronzeRaw / LandingRuwe brondata, exact zoals ontvangenIngest pipeline
SilverClean / CuratedGecleand, gevalideerd, verrijktdbt / Spark
GoldServing / MartBusiness-ready, geaggregeerddbt / SQL

Bronze laag principes

  • Data nooit verwijderen — append-only of versioned
  • Voeg technische metadata toe: _ingested_at, _source_file, _batch_id
  • Sla op in Parquet of Delta Lake formaat (niet CSV)
  • Geen business logica — puur opslag
sql — bronze tabel aanmaken (databricks/delta)
>CREATE TABLE IF NOT EXISTS bronze.orders_raw (
  order_id        STRING,
  customer_id     STRING,
  order_date      STRING,          -- ruwe data: altijd STRING in bronze
  amount          STRING,
  status          STRING,
  _ingested_at    TIMESTAMP DEFAULT current_timestamp(),
  _source_file    STRING,
  _batch_id       STRING
)
USING DELTA
PARTITIONED BY (date_trunc('day', _ingested_at))
LOCATION 'abfss://bronze@storage.dfs.core.windows.net/orders/';

Silver laag — type casting en validatie

sql — silver transformatie
>INSERT INTO silver.orders
SELECT
  order_id,
  customer_id,
  CAST(order_date AS DATE)           AS order_date,
  CAST(amount AS DECIMAL(18,2))       AS amount,
  LOWER(status)                        AS status,
  _ingested_at,
  current_timestamp()                  AS _transformed_at
FROM bronze.orders_raw
WHERE
  order_id IS NOT NULL               -- validatie
  AND amount ~ '^[0-9]+(\.[0-9]+)?$'  -- numeriek check
  AND _ingested_at > (
    SELECT MAX(_ingested_at) FROM silver.orders
  );                                   -- incrementeel laden
3

Schema ontwerp

Naming conventions, datatypes, DDL best practices

Consistente naamgeving en juiste datatypes voorkomen verwarring en zorgen voor optimale query performance.

Naming conventions

ElementConventieVoorbeeld
Schemasnake_case, enkelvoudbronze, silver, gold, mart_sales
Feitentabelfct_ prefixfct_orders, fct_revenue
Dimensietabeldim_ prefixdim_customer, dim_product
Stagingstg_ prefixstg_salesforce_orders
Primary keytabel_idorder_id, customer_id
Foreign keydim_tabel_idcustomer_id, product_id
Datum kolom_at (timestamp), _date (date)created_at, order_date
Booleanis_ prefixis_active, is_deleted

Datatypes correct gebruiken

sql — ddl best practices
>CREATE TABLE dim_customer (
  customer_sk     INTEGER         NOT NULL,   -- surrogate key: integer
  customer_id     VARCHAR(50)     NOT NULL,   -- business key: string
  customer_name   VARCHAR(200)    NOT NULL,
  email           VARCHAR(254),               -- max email lengte
  country_code    CHAR(2),                    -- ISO 3166-1: altijd 2 tekens
  annual_revenue  DECIMAL(18, 2),             -- geld: nooit FLOAT
  customer_tier   VARCHAR(20)
                  CHECK (customer_tier IN ('bronze', 'silver', 'gold')),
  is_active       BOOLEAN         DEFAULT TRUE,
  created_at      TIMESTAMP       NOT NULL,
  updated_at      TIMESTAMP,
  valid_from      DATE            NOT NULL,   -- SCD2
  valid_to        DATE,                        -- NULL = huidig actief record

  CONSTRAINT pk_dim_customer PRIMARY KEY (customer_sk)
);
⚠️ Gebruik nooit FLOAT of DOUBLE voor geldbedragen — floating point geeft afrondingsfouten. Gebruik altijd DECIMAL(p, s) of NUMERIC(p, s).
4

Ster-schema vs Sneeuwvlokschema

Kimball methodologie, denormalisatie, query performance

In een datawarehouse kies je tussen twee schema ontwerpen. Voor de meeste BI-toepassingen is het ster-schema de beste keuze.

EigenschapSter-schemaSneeuwvlokschema
StructuurDimensies gedenormaliseerdDimensies genormaliseerd (3NF)
Joins voor queryWeinig (1 niveau)Meer (meerdere niveaus)
Query performanceSnellerLangzamer
OpslagruimteMeer (redundantie)Minder
OnderhoudEenvoudigerComplexer
GebruikBI/rapportageOLTP-achtige situaties

Ster-schema voorbeeld

sql — ster-schema feitentabel
>CREATE TABLE fct_orders (
  -- Surrogate keys (foreign keys naar dimensies)
  date_sk         INTEGER        NOT NULL REFERENCES dim_date(date_sk),
  customer_sk     INTEGER        NOT NULL REFERENCES dim_customer(customer_sk),
  product_sk      INTEGER        NOT NULL REFERENCES dim_product(product_sk),
  channel_sk      INTEGER        NOT NULL REFERENCES dim_channel(channel_sk),

  -- Business key (voor traceerbaarheid)
  order_id        VARCHAR(50)    NOT NULL,

  -- Feiten (meetbare waarden)
  quantity        INTEGER        NOT NULL,
  unit_price      DECIMAL(18,2)  NOT NULL,
  discount_amount DECIMAL(18,2)  DEFAULT 0,
  revenue         DECIMAL(18,2)  NOT NULL,
  cost            DECIMAL(18,2),

  CONSTRAINT pk_fct_orders PRIMARY KEY (order_id)
);
5

ETL vs ELT aanpak

Extract-Transform-Load vs Extract-Load-Transform

De keuze tussen ETL en ELT bepaalt waar je transformatielogica plaatst en welke tools je gebruikt.

AspectETLELT
TransformatieBuiten het warehouse (ETL tool)Binnen het warehouse (SQL/dbt)
ToolsSSIS, Informatica, Talenddbt, Spark, BigQuery scripting
SchaalbaarheidBeperkt door ETL serverSchaalt mee met warehouse
KostenLicentiekosten ETL toolWarehouse compute kosten
WanneerComplexe transformaties, legacyCloud DWH, moderne aanpak
Voor nieuwe projecten is ELT de aanbevolen aanpak: laad ruwe data in de Bronze laag, transformeer binnen het warehouse met dbt of Spark. Dit is goedkoper, schaalbaarder en eenvoudiger te debuggen.
6

Partitionering & Clustering

Snowflake clustering keys, Databricks partitions, pruning

Partitionering en clustering zorgen ervoor dat queries alleen relevante data lezen (partition pruning / cluster pruning), wat enorm scheelt in scankosten en snelheid.

Partitionering in Databricks (Delta Lake)

sql — databricks delta partitionering
>-- Partitioneer feitentabellen op datum
CREATE TABLE fct_orders
USING DELTA
PARTITIONED BY (order_year INT, order_month INT)
AS SELECT
  *,
  YEAR(order_date)  AS order_year,
  MONTH(order_date) AS order_month
FROM silver.orders;

-- Z-ORDER clustering voor snellere point queries
OPTIMIZE fct_orders ZORDER BY (customer_id, product_id);

-- Vacuum (verwijder oude Delta bestanden)
VACUUM fct_orders RETAIN 168 HOURS;  -- 7 dagen history

Clustering in Snowflake

sql — snowflake clustering
>-- Clustering key instellen
ALTER TABLE fct_orders
CLUSTER BY (order_date, customer_id);

-- Clustering status controleren
SELECT SYSTEM$CLUSTERING_INFORMATION('fct_orders');

-- Handmatige recluster (bij grote wijzigingen)
ALTER TABLE fct_orders RECLUSTER;
⚠️ Snowflake: Partitioneer niet handmatig — Snowflake doet dit automatisch via Micro-partitions. Gebruik clustering keys alleen voor tabellen > 1 TB die frequent op specifieke kolommen worden gefilterd.
7

Performance tips

Materialisatie, caching, query optimalisatie, statistieken

Query optimalisatie technieken

sql — efficiënte aggregatie
>-- Slecht: subquery die per rij wordt uitgevoerd
SELECT
  customer_id,
  (SELECT COUNT(*) FROM fct_orders o2
   WHERE o2.customer_id = o1.customer_id) AS order_count
FROM dim_customer o1;

-- Goed: window function of join met aggregatie
SELECT
  c.customer_id,
  COALESCE(o.order_count, 0) AS order_count
FROM dim_customer c
LEFT JOIN (
  SELECT customer_id, COUNT(*) AS order_count
  FROM fct_orders
  GROUP BY customer_id
) o ON c.customer_id = o.customer_id;

-- Window function: running total per klant
SELECT
  customer_id,
  order_date,
  amount,
  SUM(amount) OVER (
    PARTITION BY customer_id
    ORDER BY order_date
    ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
  ) AS cumulative_revenue
FROM fct_orders;

Statistieken bijhouden (Synapse/PostgreSQL)

sql — statistieken updaten
>-- Azure Synapse: statistieken handmatig updaten
UPDATE STATISTICS fct_orders (order_date) WITH FULLSCAN;
UPDATE STATISTICS fct_orders (customer_id) WITH SAMPLE 30 PERCENT;

-- PostgreSQL: automatisch (maar handmatig forceren na bulk load)
ANALYZE fct_orders;
VACUUM ANALYZE fct_orders;
Materialiseer tussenresultaten als je complexe CTE-ketens hebt die meerdere keren worden hergebruikt. In Snowflake: gebruik een tijdelijke tabel. In Databricks: gebruik CACHE TABLE of Delta Live Tables.
8

Monitoring & Datakwaliteit

Query logs, alerting, datakwaliteitscontroles, SLA tracking

Een goed datawarehouse heeft monitoring voor zowel technische performance als datakwaliteit. Slechte data die ongedetecteerd blijft schaadt vertrouwen in alle downstream rapportages.

Datakwaliteitscontroles

sql — datakwaliteit checks
>-- 1. Volledigheid: controleer verwacht aantal rijen
SELECT
  DATE_TRUNC('day', order_date) AS dag,
  COUNT(*)                       AS aantal_orders,
  SUM(amount)                    AS totaal_omzet
FROM fct_orders
WHERE order_date >= CURRENT_DATE - INTERVAL '7 days'
GROUP BY 1
ORDER BY 1 DESC;

-- 2. Uniciteit: geen dubbele primaire sleutels
SELECT order_id, COUNT(*) AS duplicaten
FROM fct_orders
GROUP BY order_id
HAVING COUNT(*) > 1;

-- 3. Referentiële integriteit
SELECT COUNT(*) AS orphaned_orders
FROM fct_orders o
LEFT JOIN dim_customer c ON o.customer_sk = c.customer_sk
WHERE c.customer_sk IS NULL;

-- 4. Freshness: data niet ouder dan 26 uur
SELECT
  MAX(_ingested_at)                        AS laatste_ingest,
  DATEDIFF('hour', MAX(_ingested_at),
            CURRENT_TIMESTAMP())             AS uren_geleden,
  CASE WHEN
    DATEDIFF('hour', MAX(_ingested_at),
              CURRENT_TIMESTAMP()) > 26
  THEN 'ALERT: data te oud'
  ELSE 'OK'
  END AS status
FROM fct_orders;
ℹ️ Overweeg Great Expectations (Python), dbt tests of Soda Core voor geautomatiseerde datakwaliteitscontroles. Koppel alerts aan Slack of Teams via webhooks zodat het team direct op de hoogte is.
9

Checklist

Controleer voor go-live
Architectuur & Ontwerp
  • Platform gekozen en geconfigureerd
  • Bronze/Silver/Gold lagen aangemaakt
  • Naming conventions gedocumenteerd en gevolgd
  • Ster-schema geïmplementeerd in Gold laag
Data Kwaliteit
  • Null checks op verplichte kolommen
  • Uniciteitscontroles op primaire sleutels
  • Referentiële integriteitscontroles
  • Freshness monitoring actief
Performance
  • Partitionering op datum kolommen
  • Clustering keys ingesteld voor grote tabellen
  • Statistieken actueel na bulk loads
  • Query performance getest met realistische data
Beveiliging
  • Role-based access control geconfigureerd
  • PII data gemaskeerd of beperkt toegankelijk
  • Audit logging actief
  • Netwerktoegang beperkt (VNet/Private Endpoint)