Gevorderd SQL voor Data Analyst Sollicitatiegesprekken: Subqueries, Pivots en Query-optimalisatie in 2026
Beheers gevorderd SQL voor data analyst sollicitatiegesprekken in 2026. Gecorreleerde subqueries, pivottabellen met conditionele aggregatie, EXPLAIN ANALYZE-plannen en indexeringsstrategieen op PostgreSQL 17 met praktische voorbeelden.

De technische eisen aan data analisten zijn de afgelopen jaren aanzienlijk verscherpt. In 2026 beperken SQL-toetsen zich niet langer tot basale SELECT-statements en eenvoudige joins. Technologiebedrijven — van snelgroeiende scale-ups tot gevestigde consultancybureaus en multinationals — verwachten van kandidaten een diepgaande beheersing van gecorreleerde subqueries, pivottransformaties en query-optimalisatie. Deze vaardigheden vormen het belangrijkste onderscheid tussen een junior profiel en een ervaren data analyst. Dit artikel behandelt de gevorderde SQL-patronen die regelmatig terugkomen in technische evaluaties, voorzien van uitvoerbare voorbeelden op PostgreSQL 17.
SQL-interviews voor data analyst posities in 2026 richten zich op drie kerngebieden: het vermogen om complexe logica op te splitsen in leesbare subqueries of CTEs, het transformeren van rijgeorienteerde data naar pivotrapportages via conditionele aggregatie, en het aantonen van daadwerkelijk prestatiebewustzijn via EXPLAIN-plannen en indexeringsstrategieen. Optimalisatie is het gebied waar de meeste kandidaten struikelen, waardoor het een bijzonder effectief onderscheidend criterium vormt.
Gecorreleerde Subqueries versus Standaard Subqueries
Het onderscheid tussen een gecorreleerde en een standaard subquery behoort tot de fundamenten die in vrijwel elk technisch SQL-interview worden getoetst. Een standaard (niet-gecorreleerde) subquery wordt eenmalig uitgevoerd en levert een resultaat op dat onafhankelijk is van de context van de buitenste query. De database-engine berekent het resultaat van de subquery en gebruikt dit vervolgens als vaste waarde om de rijen van de hoofdquery te filteren.
In het volgende voorbeeld berekent de subquery het globale gemiddelde van de orderbedragen. Deze waarde wordt eenmalig bepaald, waarna elke rij in de tabel orders met deze enkele drempelwaarde wordt vergeleken.
-- regular_subquery_threshold.sql
-- Find all orders above the average order value
SELECT
order_id,
customer_id,
total_amount
FROM orders
WHERE total_amount > (
-- Executes once, returns a single scalar value
SELECT AVG(total_amount)
FROM orders
);Een gecorreleerde subquery daarentegen verwijst naar een of meer kolommen uit de buitenste query. De engine moet de subquery daarom opnieuw evalueren voor elke rij van de buitenste resultatenset. Dit gedrag heeft directe gevolgen voor de prestaties, vooral bij omvangrijke tabellen.
De klassieke interviewvraag die dit mechanisme illustreert: identificeer de medewerkers van wie het salaris boven het gemiddelde salaris van hun eigen afdeling ligt.
-- correlated_subquery_department.sql
-- Employees earning above their department average
SELECT
e.employee_id,
e.name,
e.department,
e.salary
FROM employees e
WHERE e.salary > (
-- Re-evaluated for each employee's department
SELECT AVG(e2.salary)
FROM employees e2
WHERE e2.department = e.department
);Deze query herberekent het gemiddelde salaris voor de afdeling van elke onderzochte medewerker. Bij een tabel met honderdduizenden rijen worden de uitvoeringskosten onacceptabel hoog. De geoptimaliseerde aanpak bestaat erin de afdelingsgemiddelden vooraf te berekenen in een CTE (Common Table Expression) en vervolgens een eenvoudige join met de hoofdtabel uit te voeren.
-- optimized_with_cte.sql
-- Same result, single pass over the data
WITH dept_avg AS (
SELECT
department,
AVG(salary) AS avg_salary
FROM employees
GROUP BY department
)
SELECT
e.employee_id,
e.name,
e.department,
e.salary
FROM employees e
JOIN dept_avg d ON e.department = d.department
WHERE e.salary > d.avg_salary;De CTE-versie doorloopt de tabel employees slechts eenmalig om de gemiddelden te berekenen en voert vervolgens de join uit. De prestatiewinst is op productievolumes vaak spectaculair. Tijdens een sollicitatiegesprek geldt het vermogen om eerst de gecorreleerde versie te schrijven en deze vervolgens spontaan te refactoren naar een CTE als een sterke indicator van technische volwassenheid.
Veelvoorkomende Subquery-patronen bij Data Analyst Interviews
Drie subquery-patronen komen systematisch terug in technische evaluaties voor data analyst functies: EXISTS voor lidmaatschapscontroles, scalaire subqueries in SELECT-clausules en afgeleide tabellen in FROM-clausules.
EXISTS versus IN — De EXISTS-clausule staakt de evaluatie zodra een overeenkomst is gevonden. Op grote datasets biedt deze short-circuit strategie een significant prestatievoordeel ten opzichte van IN, dat het volledige resultaat van de subquery materialiseert alvorens de vergelijking uit te voeren.
-- exists_vs_in.sql
-- Customers who placed at least one order in 2026 (EXISTS — preferred)
SELECT c.customer_id, c.name
FROM customers c
WHERE EXISTS (
SELECT 1
FROM orders o
WHERE o.customer_id = c.customer_id
AND o.order_date >= '2026-01-01'
);
-- Equivalent with IN (slower on large datasets)
SELECT customer_id, name
FROM customers
WHERE customer_id IN (
SELECT customer_id
FROM orders
WHERE order_date >= '2026-01-01'
);Moderne PostgreSQL-optimizers kunnen IN in bepaalde gevallen herschrijven naar een semi-join, waardoor het prestatieverschil afneemt. Desondanks blijft EXISTS de aanbevolen formulering omdat het de intentie van de ontwikkelaar duidelijker uitdrukt en gevallen met NULL-waarden correct afhandelt.
Scalaire subquery in SELECT — Dit patroon maakt het mogelijk een berekende kolom aan de resultatenset toe te voegen zonder een expliciete JOIN. De subquery retourneert een enkele waarde voor elke rij van de hoofdquery.
-- scalar_subquery_select.sql
-- Each product with its category's total revenue
SELECT
p.product_id,
p.product_name,
p.category,
(
SELECT SUM(oi.quantity * oi.unit_price)
FROM order_items oi
JOIN products p2 ON oi.product_id = p2.product_id
WHERE p2.category = p.category
) AS category_total_revenue
FROM products p;Dit patroon is geschikt om een resultatenset te verrijken met een of twee geaggregeerde kolommen. Bij meer kolommen levert een CTE gecombineerd met een JOIN leesbaardere en doorgaans performantere code op, aangezien de aggregatieberekening slechts eenmaal per categorie wordt uitgevoerd.
Klaar om je Data Analytics gesprekken te halen?
Oefen met onze interactieve simulatoren, flashcards en technische tests.
Pivotqueries met Conditionele Aggregatie
Pivotqueries vormen een onvermijdelijk onderwerp bij data analyst interviews. Het principe bestaat erin rij-voor-rij opgeslagen data om te zetten naar een kolomformaat — typisch het converteren van maanden die als waarden in een kolom zijn opgeslagen naar afzonderlijke kolommen zoals jan_revenue, feb_revenue. Hoewel sommige databasesystemen een native PIVOT-operator aanbieden, berust de universeel verwachte aanpak in interviews op CASE-expressies binnen aggregatiefuncties.
Het eerste voorbeeld toont een maandelijkse omzetpivot per product. Elke CASE-expressie isoleert de rijen die bij een specifieke maand horen, terwijl de SUM-functie de bedragen aggregeert.
-- pivot_monthly_revenue.sql
-- Monthly revenue pivot: one row per product, one column per month
SELECT
product_id,
product_name,
SUM(CASE WHEN EXTRACT(MONTH FROM order_date) = 1
THEN quantity * unit_price ELSE 0 END) AS jan_revenue,
SUM(CASE WHEN EXTRACT(MONTH FROM order_date) = 2
THEN quantity * unit_price ELSE 0 END) AS feb_revenue,
SUM(CASE WHEN EXTRACT(MONTH FROM order_date) = 3
THEN quantity * unit_price ELSE 0 END) AS mar_revenue,
SUM(CASE WHEN EXTRACT(MONTH FROM order_date) = 4
THEN quantity * unit_price ELSE 0 END) AS apr_revenue,
-- Repeat for remaining months
SUM(quantity * unit_price) AS total_revenue
FROM order_items oi
JOIN orders o ON oi.order_id = o.order_id
JOIN products p ON oi.product_id = p.product_id
WHERE o.order_date >= '2026-01-01'
GROUP BY product_id, product_name
ORDER BY total_revenue DESC;Dezelfde techniek van conditionele aggregatie is toepasbaar op multidimensionale analyses. Het volgende voorbeeld segmenteert gebruikersactiviteit per apparaattype en berekent het percentage mobiele sessies — een indicator die vaak wordt gevraagd in productgerichte interviews.
-- pivot_user_activity.sql
-- User activity: sessions by day of week and device type
SELECT
user_id,
COUNT(CASE WHEN device_type = 'mobile' THEN 1 END) AS mobile_sessions,
COUNT(CASE WHEN device_type = 'desktop' THEN 1 END) AS desktop_sessions,
COUNT(CASE WHEN device_type = 'tablet' THEN 1 END) AS tablet_sessions,
ROUND(
COUNT(CASE WHEN device_type = 'mobile' THEN 1 END) * 100.0 / COUNT(*),
1
) AS mobile_pct
FROM user_sessions
WHERE session_date >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY user_id
HAVING COUNT(*) >= 5 -- Only users with meaningful activity
ORDER BY mobile_pct DESC;De HAVING-clausule filtert gebruikers met een ontoereikend aantal sessies om statistisch significant te zijn. Dit type post-aggregatiefiltering wordt regelmatig in interviews behandeld om het begrip van de uitvoeringsvolgorde van SQL-clausules te verifieren.
Dynamische Pivot met CROSSTAB in PostgreSQL
Wanneer de categorieen van de pivot niet van tevoren bekend zijn, biedt de tablefunc-extensie van PostgreSQL de CROSSTAB-functie. Deze benadering is bijzonder nuttig voor rapporten waarvan de dimensies dynamisch varieren — bijvoorbeeld een kwartaalpivot waarvan toekomstige kwartalen nog niet in de data beschikbaar zijn.
-- crosstab_dynamic_pivot.sql
-- Enable the extension (once per database)
CREATE EXTENSION IF NOT EXISTS tablefunc;
-- Revenue by product and quarter using CROSSTAB
SELECT *
FROM crosstab(
$$
SELECT
product_name,
'Q' || EXTRACT(QUARTER FROM order_date)::TEXT AS quarter,
SUM(quantity * unit_price) AS revenue
FROM order_items oi
JOIN orders o ON oi.order_id = o.order_id
JOIN products p ON oi.product_id = p.product_id
WHERE EXTRACT(YEAR FROM o.order_date) = 2026
GROUP BY product_name, quarter
ORDER BY product_name, quarter
$$,
$$ VALUES ('Q1'), ('Q2'), ('Q3'), ('Q4') $$
) AS pivot_table(
product_name TEXT,
q1_revenue NUMERIC,
q2_revenue NUMERIC,
q3_revenue NUMERIC,
q4_revenue NUMERIC
);De eerste dollar-quoted subquery produceert de brondata in het formaat (rijcategorie, kolomcategorie, waarde). De tweede subquery definieert expliciet de verwachte kolomcategorieen. Deze scheiding stelt de planner in staat ontbrekende waarden (een product zonder verkoop in Q3, bijvoorbeeld) automatisch als NULL in te vullen.
SQL Query-optimalisatie: EXPLAIN-plannen en Prestatiediagnostiek
Het vermogen om een trage query te diagnosticeren onderscheidt operationele data analisten van louter theoretische profielen. Het EXPLAIN ANALYZE-commando is het voornaamste instrument om te begrijpen hoe de PostgreSQL-planner een query uitvoert en knelpunten te identificeren.
-- explain_analyze_example.sql
-- Analyze a slow query to identify bottlenecks
EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT)
SELECT
c.customer_id,
c.name,
COUNT(o.order_id) AS order_count,
SUM(o.total_amount) AS lifetime_value
FROM customers c
JOIN orders o ON c.customer_id = o.customer_id
WHERE o.order_date >= '2025-01-01'
GROUP BY c.customer_id, c.name
HAVING SUM(o.total_amount) > 1000
ORDER BY lifetime_value DESC;Verschillende indicatoren in de EXPLAIN-uitvoer verdienen bijzondere aandacht. Een Seq Scan (sequentiele scan) op een omvangrijke tabel wijst doorgaans op het ontbreken van een adequaat index. De vergelijking tussen geschatte rijen (rows) en daadwerkelijk verwerkte rijen (actual rows) onthult de nauwkeurigheid van de plannerstatistieken: een aanzienlijk verschil duidt op verouderde statistieken, die met een ANALYZE op de betreffende tabel kunnen worden gecorrigeerd. De BUFFERS-informatie maakt onderscheid tussen pagina's gelezen vanuit de cache (shared hit) en pagina's gelezen vanaf schijf (shared read), wat een directe indicator vormt van de efficientie van de PostgreSQL-cache.
Het geselecteerde jointype (Nested Loop, Hash Join of Merge Join) hangt af van de omvang van de datasets en de aanwezigheid van indexen. Een Hash Join wordt doorgaans gekozen voor grote joins zonder index, terwijl een Nested Loop met Index Scan optimaal is voor joins waarbij de ene tabel aanzienlijk kleiner is dan de andere.
Indexeringsstrategieen voor SQL-optimalisatie
Indexering vertegenwoordigt de meest directe hefboom om de responstijden van queries te verbeteren. Technische interviews evalueren de kennis van verschillende indextypen en het vermogen om de juiste strategie te kiezen op basis van het querypatroon.
Een enkelvoudige index op een kolom die frequent in WHERE-clausules voorkomt, verbetert de filters op die kolom. Een samengestelde index dekt meerdere kolommen, en de volgorde van kolomdeclaratie bepaalt welke queries ervan kunnen profiteren: de linker-prefixregel stelt dat de index alleen bruikbaar is wanneer de query filtert op de eerste gedeclareerde kolommen.
-- indexing_strategies.sql
-- Index on order_date: filters a small percentage of rows
CREATE INDEX idx_orders_date ON orders (order_date);
-- Composite index for queries filtering on both columns
CREATE INDEX idx_orders_customer_date
ON orders (customer_id, order_date);
-- Covering index: includes columns needed in SELECT
-- Enables Index Only Scan (no table access)
CREATE INDEX idx_orders_covering
ON orders (customer_id, order_date)
INCLUDE (total_amount, status);De dekkende index (covering index) met de INCLUDE-clausule voegt extra kolommen toe aan de bladeren van de B-tree zonder ze als zoeksleutels op te nemen. Deze techniek maakt een Index Only Scan mogelijk — de query wordt volledig door de index beantwoord zonder toegang tot de onderliggende tabel, waardoor een hele categorie I/O-bewerkingen wordt geelimineerd.
Partiele indexen bieden een aanvullende optimalisatie door slechts een subset van de tabelrijen te dekken. Bij tabellen waar het merendeel van de data gearchiveerd of inactief is, levert een partiele index op de actieve rijen een aanzienlijk kleinere en sneller doorzoekbare index op.
-- partial_index.sql
-- Only index active orders (smaller, faster index)
CREATE INDEX idx_orders_active
ON orders (customer_id, order_date)
WHERE status = 'active';
-- Only index recent data for dashboard queries
CREATE INDEX idx_orders_recent
ON orders (order_date)
WHERE order_date >= '2026-01-01';De PostgreSQL-planner gebruikt een partiele index uitsluitend wanneer de WHERE-clausule van de query overeenkomt met de conditie van de index. Deze specificiteit wordt regelmatig in interviews getoetst om het fijnmazige begrip van indexwerking te verifieren.
Het systematisch indexeren van elke kolom is een veelgemaakte fout. Elke extra index beinvloedt de schrijfbewerkingen (INSERT, UPDATE, DELETE) negatief, aangezien de engine de consistentie van alle indexen bij elke wijziging moet waarborgen. Een tabel met meer dan tien indexen op een schrijfintensieve workload ondervindt een significante degradatie. Kolommen met lage kardinaliteit (booleaanse vlaggen, statusvelden met twee of drie unieke waarden) rechtvaardigen alleen een index in het kader van een samengestelde of gerichte partiele index.
Veelvoorkomende Query-antipatronen en Hun Correcties
Bepaalde SQL-schrijfpatronen, hoewel syntactisch geldig, neutraliseren de optimalisaties van de database-engine. Het herkennen en corrigeren ervan behoort tot de vaardigheden die in gevorderde technische interviews worden geevalueerd.
Het toepassen van een functie op een geindexeerde kolom vormt het meest voorkomende antipatroon. Wanneer een functie de waarde van een kolom transformeert voor de vergelijking, kan de planner de bestaande index op die kolom niet meer gebruiken, aangezien de opgeslagen waarden in de index niet meer overeenkomen met de vergeleken waarden.
-- anti_patterns.sql
-- BAD: function on indexed column disables the index
SELECT * FROM orders
WHERE EXTRACT(YEAR FROM order_date) = 2026;
-- GOOD: range comparison uses the index
SELECT * FROM orders
WHERE order_date >= '2026-01-01'
AND order_date < '2027-01-01';De herschrijving naar een bereiksvergelijking behoudt het indexgebruik en levert een functioneel identiek resultaat op. Deze refactoring biedt op omvangrijke tabellen vaak een aanzienlijke prestatiewinst.
Het tweede antipatroon betreft het gebruik van NOT IN met subqueries die mogelijk NULL-waarden bevatten. Deze valkuil is bijzonder verraderlijk omdat de query zonder foutmelding wordt uitgevoerd maar een lege resultatenset retourneert. In de driewaardige SQL-logica levert elke vergelijking met NULL het resultaat UNKNOWN op, en NOT IN gedraagt zich als een conjunctie van NOT EQUAL-condities — als een daarvan UNKNOWN is, geldt dat voor de gehele conditie.
-- not_in_null_trap.sql
-- BAD: returns empty if any customer_id is NULL in subquery
SELECT * FROM customers
WHERE customer_id NOT IN (
SELECT customer_id FROM orders
);
-- GOOD: NOT EXISTS handles NULLs correctly
SELECT * FROM customers c
WHERE NOT EXISTS (
SELECT 1 FROM orders o
WHERE o.customer_id = c.customer_id
);NOT EXISTS behandelt NULL-waarden correct omdat de conditie betrekking heeft op het bestaan van een overeenkomst, en niet op een waardenvergelijking. Dit patroon verdient systematisch de voorkeur boven NOT IN voor uitsluitingen op basis van subqueries.
Het beheersen van gecorreleerde subqueries, CTEs en SQL-optimalisatie vergt regelmatige oefening met realistische interviewscenario's. Het combineren van subquery-refactoring met de analyse van EXPLAIN-plannen ontwikkelt geleidelijk het diagnostische inzicht dat recruiters zoeken bij kandidaten op midden- tot seniorniveau.
Klaar om je Data Analytics gesprekken te halen?
Oefen met onze interactieve simulatoren, flashcards en technische tests.
Conclusie
De voorbereiding op SQL-interviews voor data analyst functies in 2026 berust op de beheersing van meerdere complementaire domeinen. De belangrijkste aandachtspunten:
- Gecorreleerde subqueries worden voor elke rij van de buitenste resultatenset opnieuw uitgevoerd — refactoring naar CTEs of vensterfuncties verbetert de prestaties op omvangrijke tabellen aanzienlijk
- EXISTS presteert beter dan IN voor lidmaatschapscontroles, vooral wanneer de subquery een groot aantal rijen retourneert of NULL-waarden bevat
- Conditionele aggregatie met CASE vormt de universele en draagbare pivottechniek — het beheersen ervan is een voorwaarde alvorens SGBD-specifieke PIVOT-syntaxen aan te pakken
- EXPLAIN ANALYZE onthult het werkelijke uitvoeringsplan van de query; de aandacht dient te gaan naar Seq Scans, de nauwkeurigheid van rijschattingen en de geselecteerde joinstrategie
- De kolomvolgorde in samengestelde indexen moet overeenkomen met de filterpatronen van de queries — de linker-prefixregel is altijd van toepassing
- Partiele en dekkende indexen verminderen de I/O-bewerkingen voor specifieke querypatronen zonder de overhead van een index die de gehele tabel dekt
- Functies toegepast op geindexeerde kolommen, NOT IN met NULL-waarden en SELECT * in productiequerys vormen antipatronen die systematisch dienen te worden geelimineerd
Begin met oefenen!
Test je kennis met onze gespreksimulatoren en technische tests.
Tags
Delen
Gerelateerde artikelen

SQL voor Data-Analisten: Vensterfuncties, CTE's en Geavanceerde Query's
Een uitgebreide gids over SQL-vensterfuncties, Common Table Expressions (CTE's) en geavanceerde querypatronen voor data-analisten. Met praktische codevoorbeelden van ROW_NUMBER, RANK, LAG, LEAD, NTILE en recursieve CTE's.

De 25 Meest Gestelde Data Analytics Sollicitatievragen in 2026
De meest voorkomende data analytics sollicitatievragen in 2026: SQL, Python, Power BI, statistiek en gedragsvragen met uitgebreide antwoorden en codevoorbeelden.

Pandas 3.0 in 2026: Nieuwe API's, Breaking Changes en Interviewvragen
Pandas 3.0 introduceert Copy-on-Write als standaardgedrag, een PyArrow-ondersteund string dtype en de nieuwe pd.col() expression builder. Dit artikel behandelt de belangrijkste wijzigingen, migratiepatronen en interviewvragen voor data engineers.