Multivariate statistieken voor gecorreleerde kolommen
Maak CREATE STATISTICS-objecten om afhankelijkheden vast te leggen waarvan de planner ten onrechte aanneemt dat ze onafhankelijk zijn.
Multivariate statistieken voor gecorreleerde kolommen is een gratis Prestaties en queryoptimalisatie in PostgreSQL-les op CoddyKit. Dit is les 2 van 4. Je kunt 3 lessen uit dit leerpad gratis volledig lezen — daarna ontgrendelt CoddyKit PRO alle lessen, plus praktische oefeningen met een ingebouwde code-editor en een AI-tutor die 24/7 beschikbaar is. Deze les maakt deel uit van het leertraject Prestaties en queryoptimalisatie in PostgreSQL. Je voortgang wordt gesynchroniseerd op het web en in de CoddyKit-app. De cursus Prestaties en queryoptimalisatie in PostgreSQL bevat in totaal 4 lessen.
De aanname van onafhankelijkheid
Wanneer PostgreSQL schat hoeveel rijen een query oplevert, steunt het op statistieken per kolom die zijn opgeslagen in pg_statistic. Om predicaten op meerdere kolommen te combineren, maakt de queryplanner een cruciale vereenvoudigende aanname: de kolommen zijn statistisch onafhankelijk.
Bij onafhankelijkheid wordt de selectiviteit van WHERE a = 1 AND b = 2 berekend als sel(a=1) * sel(b=2). Die vermenigvuldiging is snel en correct — maar alleen wanneer de kolommen echt niets met elkaar te maken hebben.
In echte schema's zijn kolommen vaak gecorreleerd: een stad impliceert een postcode, een product impliceert een categorie en een besteldatum impliceert een boekhoudkundig kwartaal. Wanneer de queryplanner selectiviteiten voor gecorreleerde kolommen vermenigvuldigt, komt zijn schatting veel lager uit dan de werkelijkheid.
Hoe slechte schattingen je benadelen
Een schatting van het aantal rijen die meerdere ordes van grootte afwijkt, stuurt de queryplanner naar het verkeerde plan:
- Onderschatting → de queryplanner kiest een geneste lus in de verwachting van 3 rijen, maar er komen 300.000 aan → de lus voert zijn binnenste kant honderdduizenden keren uit.
- Onderschatting → de queryplanner kiest een indexscan + ophalen uit de heap in plaats van één sequentiële scan, die goedkoper zou zijn geweest.
- Verkeerde joinvolgorde → een groot tussenresultaat wordt vroeg gematerialiseerd, waardoor het geheugen volloopt en gegevens naar schijf worden weggeschreven.
Het symptoom dat je in EXPLAIN ANALYZE ziet, is een groot verschil tussen rows= (geschat) en actual rows=. Dat verschil is het signaal dat correlatie de oorzaak kan zijn.
EXPLAIN ANALYZE
SELECT * FROM addresses
WHERE city = 'New York'
AND state = 'NY';De verkeerde schatting zichtbaar maken
Neem een tabel addresses waarin city functioneel bepalend is voor state — elke rij met city = 'New York' heeft ook state = 'NY'. De twee predicaten selecteren dezelfde rijen, dus de gecombineerde selectiviteit is gelijk aan alleen sel(city).
De queryplanner vermenigvuldigt ze echter: sel(city) * sel(state), wat een schatting oplevert die 10× of 100× te klein kan zijn. Let in de onderstaande uitvoer van EXPLAIN ANALYZE op het verschil tussen het geschatte en werkelijke aantal rijen op het scanknooppunt.
EXPLAIN ANALYZE
SELECT count(*) FROM addresses
WHERE city = 'New York'
AND state = 'NY';
-- Seq Scan ... (rows=12 ...) (actual ... rows=8400 ...)
-- ^estimate ^realityCREATE STATISTICS doet zijn intrede
Met PostgreSQL 10+ kun je de queryplanner over relaties tussen kolommen leren met objecten voor uitgebreide statistieken, die je aanmaakt via CREATE STATISTICS.
Een object voor uitgebreide statistieken benoemt een reeks kolommen (of expressies) in één tabel en één of meer typen statistieken die daarover moeten worden verzameld. Nadat het object is aangemaakt en geanalyseerd, raadpleegt de queryplanner deze multivariate statistieken in plaats van blindweg selectiviteiten per kolom te vermenigvuldigen.
De drie typen zijn:
- ndistinct — aantal verschillende combinaties van de vermelde kolommen.
- dependencies — graden van functionele afhankelijkheid tussen kolommen.
- mcv — lijsten met meest voorkomende waarden voor de kolomgroep.
CREATE STATISTICS stat_addr_city_state
ON city, state
FROM addresses;
ANALYZE addresses;Functionele afhankelijkheden
Het type dependencies legt functionele afhankelijkheden vast: hoe sterk de waarde van de ene kolom de waarde van een andere kolom impliceert. PostgreSQL slaat voor elke richting een graad tussen 0 en 1 op.
Voor onze tabel heeft city → state een graad dicht bij 1.0 (als je de stad kent, ligt de staat volledig vast), terwijl state → city veel lager is (een staat heeft veel steden).
Wanneer de queryplanner WHERE city = ? AND state = ? evalueert en een sterke afhankelijkheid city → state vindt, stopt hij met vermenigvuldigen en houdt hij in essentie de selectiviteit van de bepalende kolom aan.
CREATE STATISTICS stat_addr_deps (dependencies)
ON city, state
FROM addresses;
ANALYZE addresses;De opgeslagen afhankelijkheden bekijken
Na ANALYZE staan de berekende waarden in de catalogusweergave pg_stats_ext (onbewerkte vorm) en in pg_stats_ext_exprs voor statistieken over expressies. De kolom dependencies toont de graad voor elke richting.
Een graad van of dicht bij 1.000000 voor "1 => 2" (kolom 1 impliceert kolom 2) bevestigt een vrijwel perfecte functionele afhankelijkheid — precies de situatie waarin de aanname van onafhankelijkheid je benadeelde.
SELECT statistics_name,
attnames,
dependencies
FROM pg_stats_ext
WHERE statistics_name = 'stat_addr_deps';
-- dependencies: {"1 => 2": 1.000000, "2 => 1": 0.140000}ndistinct voor GROUP BY en joins
Het type ndistinct registreert het aantal verschillende combinaties van de vermelde kolommen. Zonder dit type schat de queryplanner het aantal verschillende combinaties als het product van de aantallen verschillende waarden per kolom, wat bij gecorreleerde kolommen sterk te hoog uitvalt.
Dit is vooral belangrijk voor GROUP BY a, b, c (het schatten van het aantal groepen) en voor gegroepeerde aggregaties die een hash-aggregatie voeden. Een verkeerde schatting van het aantal groepen leidt tot te kleine hashtabellen en wegschrijven naar schijf, of tot een verkeerd gekozen sorteergebaseerde aggregatie.
CREATE STATISTICS stat_sales_ndist (ndistinct)
ON region, country, city
FROM sales;
ANALYZE sales;
EXPLAIN
SELECT region, country, city, count(*)
FROM sales
GROUP BY region, country, city;MCV voor scheef verdeelde combinaties
Functionele afhankelijkheden gaan uit van een uniforme relatie voor de hele tabel. Soms is de correlatie echter waardespecifiek — bepaalde combinaties komen extreem vaak voor, terwijl andere nooit voorkomen. Daar komt het mcv (meest voorkomende waarden) goed van pas.
Een MCV-lijst voor een kolomgroep slaat de werkelijke veelvoorkomende combinaties en hun frequenties op. Daardoor kan de planner predicaten zoals WHERE category = 'A' AND status = 'shipped' schatten op basis van de werkelijk waargenomen frequentie van dat paar, in plaats van een afgeleide benadering.
MCV is de krachtigste, maar ook de opslagintensiefste soort. Gebruik deze wanneer afhankelijkheden alleen de schatting niet verbeteren.
CREATE STATISTICS stat_orders_mcv (mcv)
ON category, status
FROM orders;
ANALYZE orders;Soorten combineren in één object
Je kunt meerdere soorten opvragen in één statistiekobject. Als je de lijst met soorten helemaal weglaat, bouwt PostgreSQL alle toepasselijke soorten voor die kolommenset.
Een veelgebruikte, praktische aanpak is om ndistinct, dependencies, mcv samen op te geven voor een set kolommen die samen voorkomen in zowel WHERE- als GROUP BY-clausules. Met één ANALYZE wordt alles gevuld.
Let op: mcv en dependencies ondersteunen maximaal een beperkt aantal kolommen. Houd statistiekobjecten bovendien gericht op kolommen die daadwerkelijk samen worden opgevraagd — niet op elk kolommenpaar in de tabel.
CREATE STATISTICS stat_addr_all (ndistinct, dependencies, mcv)
ON city, state, zip
FROM addresses;
ANALYZE addresses;Statistieken voor expressies
PostgreSQL 14+ breidt CREATE STATISTICS uit met ondersteuning voor expressies, niet alleen voor losse kolommen. Als je query's filteren op date_trunc('month', created_at) of lower(email), heeft de planner normaal gesproken geen statistieken voor die berekende waarde en valt deze terug op een algemene schatting.
Een statistiekobject met één expressie geeft de planner statistieken per expressie. Een object met meerdere kolommen waarin expressies en kolommen worden gecombineerd, legt de correlatie vast tussen een berekende waarde en een opgeslagen kolom.
CREATE STATISTICS stat_login_expr
ON lower(email), date_trunc('day', created_at)
FROM logins;
ANALYZE logins;Werkproces, onderhoud en opruimen
Uitgebreide statistieken worden niet automatisch aangemaakt — je maakt ze bewust aan op basis van vastgestelde onjuiste schattingen. Een betrouwbaar werkproces:
- Zoek een query waarvan
EXPLAIN ANALYZEeen groot verschil tussen de schatting en de werkelijke waarde laat zien bij een predicaat met meerdere kolommen. - Maak een statistiekobject aan voor precies die gecorreleerde kolommen.
- Voer
ANALYZEuit op de tabel (of wacht tot de analyse door autovacuum wordt uitgevoerd) om het object te vullen. - Voer
EXPLAIN ANALYZEopnieuw uit en controleer of de schatting nu de werkelijkheid volgt.
Statistiekobjecten worden bij elke ANALYZE vernieuwd en blijven na het aanmaken dus automatisch actueel. Verwijder objecten die je niet meer nodig hebt met DROP STATISTICS, zodat je niet blijft betalen voor de analysekosten.
DROP STATISTICS IF EXISTS stat_addr_deps;Korte controle: de juiste soort kiezen
Je hebt de query SELECT count(*) FROM addresses WHERE city = $1 AND state = $2. EXPLAIN ANALYZE laat zien dat de planner 15 rijen schat, terwijl er in werkelijkheid 9.000 overeenkomen, omdat city state volledig bepaalt. Welke soort uitgebreide statistiek corrigeert deze schatting het meest direct?
Samenvatting
De planner gaat ervan uit dat kolommen onafhankelijk zijn en vermenigvuldigt de selectiviteit per kolom — daardoor schat hij bij gecorreleerde kolommen het aantal rijen te laag in, wat tot slechte plannen leidt.
- CREATE STATISTICS leert de planner relaties tussen meerdere kolommen.
- dependencies corrigeert te lage schattingen bij gelijkheidsfilters door functionele afhankelijkheden (bijvoorbeeld city → state).
- ndistinct corrigeert schattingen van unieke combinaties voor
GROUP BYen gegroepeerde aggregaties. - mcv legt waardespecifieke scheve combinaties vast voor de nauwkeurigste selectiviteit per paar.
- Maak statistieken aan voor expressies (PG14+), vernieuw ze met
ANALYZE, controleer ze metEXPLAIN ANALYZEen bekijk de resultaten inpg_stats_ext.
Richt je alleen op kolommen die echt samen worden opgevraagd en controleer daarna of het verschil tussen de schatting en de werkelijke waarde kleiner wordt.
Leer SQL met een AI-tutor — gratis
Schrijf echte code en voer die uit in je browser, krijg direct hulp van een AI-tutor die 24/7 beschikbaar is en ga verder waar je gebleven bent op het web of in de app.
- Cursussen
- 22
- Lessen
- 88
Veelgestelde vragen
Is de les “Multivariate statistieken voor gecorreleerde kolommen” gratis?
Ja — je kunt hier op het web alle 3 lessen van het leerpad Prestaties en queryoptimalisatie in PostgreSQL, waaronder “Multivariate statistieken voor gecorreleerde kolommen”, gratis volledig lezen. Daarna ontgrendelt CoddyKit PRO alle lessen, plus interactieve oefeningen met een ingebouwde code-editor en een AI-tutor die 24/7 beschikbaar is. De cursus Prestaties en queryoptimalisatie in PostgreSQL bevat in totaal 4 lessen.
Wat leer ik in “Multivariate statistieken voor gecorreleerde kolommen”?
Maak CREATE STATISTICS-objecten om afhankelijkheden vast te leggen waarvan de planner ten onrechte aanneemt dat ze onafhankelijk zijn. Je oefent met Prestaties en queryoptimalisatie in PostgreSQL door code rechtstreeks in de browser uit te voeren. Een AI-begeleider die 24/7 beschikbaar is beantwoordt je vragen terwijl je de les doorwerkt.
Heb ik ervaring nodig om met Prestaties en queryoptimalisatie in PostgreSQL te beginnen?
Ervaring vooraf is niet nodig. Prestaties en queryoptimalisatie in PostgreSQL op CoddyKit is opgebouwd voor beginners tot gevorderden, zodat je hier of bij het begin kunt starten en in je eigen tempo kunt leren. Dit is les 2 van 4.
Hoe lang duurt de les “Multivariate statistieken voor gecorreleerde kolommen”?
De meeste lessen van CoddyKit duren ongeveer 5–10 minuten. Elke les is kort en interactief, zodat je gestaag vooruitgaat en op het web en in de app precies verdergaat waar je was gebleven.
Kan ik code schrijven en uitvoeren in deze les over Prestaties en queryoptimalisatie in PostgreSQL?
Ja. Elke les over Prestaties en queryoptimalisatie in PostgreSQL bevat een ingebouwde code-editor, zodat je rechtstreeks in je browser echte code kunt schrijven en uitvoeren en direct feedback van AI krijgt — lokale installatie is niet nodig.
Alle lessen in deze cursus
- Hoe de planner aantallen rijen schat
- Multivariate statistieken voor gecorreleerde kolommen
- MCV- en N-Distinct-correcties
- Schattingen vergelijken met werkelijke rijen