Tähtimalli ja lumihiutalemalli Power BI:ssä: viisi esimerkkiä

Kirjoittanut Samu Lahdenperä · Julkaistu · Päivitetty

Sama myyntiaineisto voidaan mallintaa neljällä eri tavalla — kahtena tähtimallina (star schema) ja kahtena lumihiutalemallina (snowflake schema). Kummassakin mallityypissä toinen versio on yksiselitteinen ja toinen moniselitteinen. Tähtimallissa moniselitteisyys syntyy duplikaattiavaimista. Lumihiutalemallissa moniselitteisyys syntyy relaatioketjusta: kun hierarkia on hajautettu useampaan tauluun, yhteen kysymykseen vastaamiseen tarvitaan useampi JOIN — ja jokainen ylimääräinen relaatioaskel on yksi lisää paikkoja, joissa filtteri voi toimia eri tavalla kuin odottaa. Viidentenä esimerkkinä on sekasikiömalli: se mitä syntyy kun malliin on vuosien varrella lisätty kaikki pyydetty miettimättä arkkitehtuuria. Esimerkit on järjestetty huonoimmasta parhaaseen — liikkeelle lähdetään sekasikiömallista ja lopussa on Power BI:hin ja muihin Self Service BI- ja analytiikkatarkoituksiin parhaiten sopiva yksiselitteinen tähtimalli.

1. Sekasikiömalli – orgaanisesti kasvanut ✗✗ vältä

Tämä malli on rakennettu vuosien varrella toteuttamalla jokainen pyyntö sellaisenaan. "Lisää vain uusi sarake." "Tee uusi taulu, älä koske vanhaan." "Jätä vanha versio varmuuden vuoksi." Jokainen yksittäinen päätös on ollut helppo ja nopea. Kokonaisuudesta on tullut se mitä näet alla. Malli on nimetty jeesmiesten mukaan: tekijöiden, jotka hyväksyvät jokaisen ylhäältä päin tulevan pyynnön kyseenalaistamatta mitään ääneen.

flowchart LR MU["f_myynnit_uusi\n(faktataulu)"] MV["f_myynnit_vanha\n(vanha faktataulu ⚠)"] A["d_asiakas"] A2["d_asiakkaat2\n(toinen asiakasrekisteri ⚠)"] T["d_tuote"] TK["d_tuote_kategoria"] P["d_paiva"] K["d_kalenteri\n(toinen päivämäärä ⚠)"] MR["f_myynti_raportti\n(kopio faktataulusta ⚠)"] A -->|"1 → *"| MU A2 -->|"moni-moneen ⚠"| MU A -->|"vanha yhteys ⚠"| MV T -->|"1 → *"| MU TK -->|"lumihiutale"| T P -->|"1 → *"| MU K -->|"moni-moneen ⚠"| MU MR -->|"fakta → fakta ⚠"| MU style MV fill:#fff8e1,stroke:#e6a817 style A2 fill:#fff8e1,stroke:#e6a817 style K fill:#fff8e1,stroke:#e6a817 style MR fill:#fff8e1,stroke:#e6a817
Sekasikiömalli: kymmenen taulua, kaksi faktaa, kaksi asiakastaulua, kaksi päivämäärätaulua, fakta-fakta-yhteys ja moni-moneen-relaatioita. Keltaiset solmut ovat ongelmallisia.
Mitä tässä mallissa on vialla — taulu kerrallaan:
Taulu tai kenttä Ongelma
f_myynnit_vanha Vanha faktataulu jätetty malliin "varmuuden vuoksi". Kukaan ei enää tiedä onko se käytössä, mutta sen poistaminen tuntuu riskiltä.
d_asiakkaat2 Toinen asiakasrekisteri lisätty kun ensimmäisessä ei ollut segmenttikenttää. Avain on tekstimuotoinen (string asiakasId) joten se ei yhdisty siististi — tuloksena moni-moneen.
d_kalenteri Toinen päivämäärädimensio luotu kun alkuperäistä d_paiva-taulua ei löydetty. Molemmat ovat nyt mallissa, molempia käytetään eri laskureissa.
f_myynti_raportti Faktataulu joka viittaa toiseen faktatauluun. Tehty nopeuttamaan "yhtä raporttia". Luo fakta-fakta-yhteyden joka rikkoo filterin leviämisen.
f_myynnit_uusi[asiakasNimi] Asiakkaan nimi on kopioitu suoraan faktatauluun koska "se oli helpompaa". Nyt sama asiakas saattaa esiintyä kahdella eri nimellä eri riveillä.
f_myynnit_uusi[myyntisumma_korjattu] Joku korjasi virheelliset luvut uuteen sarakkeeseen alkuperäisen viereen. Alkuperäinen jäi malliin. Raporttien tekijät eivät aina tiedä kumpaa käyttää.
f_myynnit_uusi[kustannuspaikka] Kustannuspaikkatieto lisätty suoraan faktatauluun eikä omaan dimensioonsa. Toistuu joka myyntirivillä — ja jos kustannuspaikka muuttuu, se on korjattava tuhansiin riveihin.
d_tuote[kategoriaNimi] Kategorianimikin on kopioitu Tuote-tauluun vaikka d_tuote_kategoria-taulu on jo olemassa. Nyt tieto on kahdessa paikassa ja ne voivat eriytyä.
Dataneuvoksen mielipide

Sekasikiömalli ei synny huolimattomuudesta — se syntyy siitä, että jokainen yksittäinen pyyntö on toteutettu järkevästi ilman kokonaiskuvaa. Ongelma ei ole yksittäisissä päätöksissä vaan siinä, että tietomallilla ei ole omistajaa joka katsoisi kokonaisuutta. Kun sellainen löytyy, ensimmäinen työ on yleensä sama: selvitä mitä tauluja oikeasti käytetään, poista loput, ja yhdistä duplikaatit yhdeksi.

2. Tähtimalli – moniselitteinen ✗ kaksoislaskennan riski

Moniselitteisyys syntyy useimmiten siitä, että dimensiotaulun pääavain ei ole uniikki: sama arvo toistuu usealla rivillä. Tällöin yksi-moneen-suhde rikkoutuu — Power BI joutuu käyttämään moni-moneen-relaatiota, jossa yksi myyntirivi voi vastata useampaan Asiakas-riviin. Raportti voi kaksoislaskea myynnit tai antaa vääriä summia.

erDiagram d_asiakas { int asiakasId "ei uniikki" string nimi string kaupunki } f_myynnit { int myyntiId PK int asiakasId FK decimal myyntisumma } d_asiakas }o--o{ f_myynnit : "moni-moneen"
Moniselitteinen relaatio: Asiakas-dimensiossa duplikaattiavaimia → suhde muuttuu moni-moneen-relaatioksi.
Esimerkki: sama asiakasId kahdella eri rivillä (virhe)
Asiakas-dimensio — asiakasId=1 toistuu kahdesti eri tiedoilla
asiakasId nimi kaupunki
1 Matti Meikäläinen Helsinki
1 Matti M. Espoo
2 Liisa Virtanen Tampere
Myynnit-faktataulu — asiakasId=1 löytää kaksi vastaavaa Asiakas-riviä
myyntiId (PK) asiakasId (FK) myyntisumma
101 1 250 €

Matriisiraportti Asiakas-dimensiolla voi näyttää: Matti Meikäläinen = 250 € ja Matti M. = 250 €, yhteensä 500 € — vaikka oikea summa on 250 €. Kaksoislaskenta syntyy moni-moneen-relaatiosta.

Dataneuvoksen mielipide

Tarkista dimensiotaulujen PK-uniikkius DAX-mittarilla: COUNTROWS(d_asiakas) = DISTINCTCOUNT(d_asiakas[asiakasId]). Jos luvut eroavat, dimensiossasi on duplikaattiavaimia. Yhdistä tai poista duplikaatit ETL-vaiheessa ennen lataamista — älä yritä korjata moni-moneen-relaatiota Power BI:n sisällä, sillä se rikkoo enemmän asioita kuin korjaa.

3. Lumihiutalemalli – moniselitteinen ⚠ välttäen

Lumihiutalemallissa Tuote-dimensio on jaettu hierarkkisesti kolmeen tauluun: d_tuoteperhed_tuotekategoriad_tuote. Kysymykseen "kuuluuko tämä myynti Elektroniikkaan?" vastatakseen Power BI:n täytyy kulkea kolmen taulun läpi: f_myynnitd_tuoted_tuotekategoriad_tuoteperhe. Jokainen relaatioaskel on paikka, jossa filterin suunta tai aktiivisuus voi yllättää — ja DAX-laskurista tulee vaikeampi kirjoittaa ja lukea kuin yhden taulun versiossa.

erDiagram d_tuoteperhe { int perheId PK string perheNimi } d_tuotekategoria { int kategoriaId PK string kategoriaNimi int perheId FK } d_tuote { int tuoteId PK string tuoteNimi int kategoriaId FK } f_myynnit { int myyntiId PK int tuoteId FK decimal myyntisumma } d_tuoteperhe ||--o{ d_tuotekategoria : "1 → *" d_tuotekategoria ||--o{ d_tuote : "1 → *" d_tuote ||--o{ f_myynnit : "1 → *"
Moniselitteinen lumihiutalemalli: Tuote-hierarkia kolmessa taulussa. Myynnin summaamiseen tarvitaan kolmen relaation ketju.
Kolme taulua — filterin polku on pitkä:
d_tuoteperhe
perheId (PK) perheNimi
1 Elektroniikka
d_tuotekategoria — viittaa d_tuoteperheeseen FK:lla
kategoriaId (PK) kategoriaNimi perheId (FK)
10 Kannettavat tietokoneet 1
11 Älypuhelimet 1
d_tuote — viittaa d_tuotekategoriaan FK:lla
tuoteId (PK) tuoteNimi kategoriaId (FK)
100 ProBook 14 10
101 Galaxy S25 11
102 IdeaPad 5 10

f_myynnit filtteröityy d_tuoteperhe-taululla vasta kolmen relaation kautta: f_myynnitd_tuoted_tuotekategoriad_tuoteperhe. DAX-laskuri CALCULATE(SUM(f_myynnit[myyntisumma]), d_tuoteperhe[perheNimi] = "Elektroniikka") toimii, mutta se edellyttää kaikkien kolmen relaation olevan aktiivisia ja filtterin kulkevan oikein suuntaan.

4. Lumihiutalemalli – yksiselitteinen (litistetty) ✓ toimii

Kun sama Tuote-hierarkia litistetään yhdeksi leveäksi dimensioksi, rakenne muuttuu yksiselitteiseksi: f_myynnitd_tuote on yksi relaatio, ja d_tuote[perheNimi] löytyy suoraan samasta taulusta. Filterin polku on lyhyt ja läpinäkyvä. Data toistuu (jokainen Elektroniikka-tuote sisältää kentässä tekstin "Elektroniikka"), mutta VertiPaq pakkaa toistuvat arvot tehokkaasti eikä toisteisuus kasvata muistikokoa merkittävästi.

Huomaa: litistetty lumihiutalemalli on rakenteeltaan tähtimalli. Kun hierarkian taulut litistetään yhdeksi leveäksi dimensioksi, jäljelle jää faktataulu ja sen ympärille dimensiot, jokainen yhden relaation päässä. Litistäminen ei siis tuota uutta mallityyppiä, se tuottaa tähtimallin — vain lähtötilanne on eri.

erDiagram d_tuote { int tuoteId PK string tuoteNimi string kategoriaNimi string perheNimi } f_myynnit { int myyntiId PK int tuoteId FK decimal myyntisumma } d_tuote ||--o{ f_myynnit : "1 → *"
Yksiselitteinen lumihiutalemalli litistettynä: kaikki Tuote-hierarkian tiedot yhdessä taulussa. Filterin polku on yksi relaatio.
Yksi taulu — filterin polku on suora:
Tuote-dimensio — litistetty, kaikki hierarkiatiedot yhdessä
tuoteId (PK) tuoteNimi kategoriaNimi perheNimi
100 ProBook 14 Kannettavat tietokoneet Elektroniikka
101 Galaxy S25 Älypuhelimet Elektroniikka
102 IdeaPad 5 Kannettavat tietokoneet Elektroniikka

DAX-laskuri CALCULATE(SUM(f_myynnit[myyntisumma]), d_tuote[perheNimi] = "Elektroniikka") toimii yhdellä suoralla relaatiolla — ei ketjutusta, ei epävarmuutta filterin suunnasta.

5. Tähtimalli – yksiselitteinen ✓ suositus

Toimivassa tähtimallissa jokaisen dimensiotaulun pääavain (PK) on uniikki: sama avaintieto esiintyy täsmälleen kerran. Tämä tekee jokaisen relaation yksi-moneen-suhteeksi — yksi Asiakasrivi vastaa useaan Myyntiriviin, muttei koskaan toisinpäin. Power BI pystyy ratkaisemaan kaikki DAX-laskelmat yksikäsitteisesti.

erDiagram d_asiakas { int asiakasId PK string nimi string kaupunki } d_tuote { int tuoteId PK string tuoteNimi string kategoria } d_myymala { int myymalaId PK string myymala string kaupunki } d_aika { int aikaId PK date paiva int vuosi string kuukausi } d_myyja { int myyjäId PK string nimi } f_myynnit { int myyntiId PK int asiakasId FK int tuoteId FK int myymalaId FK int aikaId FK int myyjäId FK decimal myyntisumma int kappalemäärä } d_asiakas ||--o{ f_myynnit : "1 → *" d_tuote ||--o{ f_myynnit : "1 → *" d_myymala ||--o{ f_myynnit : "1 → *" d_aika ||--o{ f_myynnit : "1 → *" d_myyja ||--o{ f_myynnit : "1 → *"
Yksiselitteinen tähtimalli: viisi dimensiota, yksi faktataulu, kaikki relaatiot yksi-moneen. d_myymala = Myymälä, d_myyja = Myyjä.
Esimerkki: Asiakas-dimensio (PK uniikki) ja Myynnit-faktataulu
Asiakas-dimensio — jokainen asiakasId esiintyy täsmälleen kerran
asiakasId (PK) nimi kaupunki
1 Matti Meikäläinen Helsinki
2 Liisa Virtanen Tampere
3 Juhani Korhonen Oulu
Myynnit-faktataulu — FK viittaa aina täsmälleen yhteen Asiakas-riviin
myyntiId (PK) asiakasId (FK) tuoteId (FK) myyntisumma
101 1 10 250 €
102 2 11 80 €
103 1 10 310 €

Matin kokonaismyynti: 250 + 310 = 560 €. Tulos on yksiselitteinen — yksi asiakas, yksi summa.

Vertailu tiivistettynä

Olennainen havainto: jokainen näistä viidestä mallista pystyy vastaamaan samoihin kysymyksiin, ja jokaisesta saa loppukäyttäjälle täysin samat tulokset ja täysin samat arvot — sekasikiömallistakin, kunhan laskurit kirjoitetaan riittävän huolellisesti sen rakenteen ehdoilla. Mallien ero ei siis ole siinä, mitä niillä on mahdollista laskea. Ero on siinä, kuinka nopeasti laskenta toimii, kuinka helppoa laskureita on kirjoittaa ja kuinka todennäköisesti tulos on oikein ensimmäisellä yrityksellä. Tähtimalli on näistä nopein ja paras analytiikkaan, self-serviceen ja AI-valmiuteen.

Malli Relaatiotyyppi PK uniikki Toisteisuus Sopii Power BI:hin
Sekasikiömalli Sekalainen Vaihtelee Kyllä, kaikkialla Ei ✗✗
Tähtimalli – moniselitteinen Moni-moneen Ei (duplikaatit) Kyllä Ei ✗
Lumihiutalemalli – moniselitteinen Yksi-moneen (ketjussa) Kyllä Ei Välttäen ⚠
Lumihiutalemalli – yksiselitteinen (litistetty) Yksi-moneen Kyllä Kyllä (pakattuna) Kyllä ✓
Tähtimalli – yksiselitteinen Yksi-moneen Kyllä Ei Kyllä ✓
Dataneuvoksen mielipide