Richtlijnen voor veel-op-veel-relaties
Dit artikel is bedoeld voor u als datamodelleur die werkt met Power BI Desktop. Hierin worden drie verschillende veel-op-veel-modelleringsscenario's beschreven. Het biedt u ook richtlijnen over hoe u succesvol voor hen kunt ontwerpen in uw modellen.
Notitie
In dit artikel wordt geen inleiding tot modelrelaties behandeld. Als u niet volledig bekend bent met relaties, eigenschappen of hoe u deze configureert, raden we u aan eerst de modelrelaties te lezen in Power BI Desktop artikel.
Het is ook belangrijk dat u inzicht heeft in het ontwerp van sterrenschema's. Voor meer informatie, zie Begrijp het sterschema en het belang ervan voor Power BI.
Er zijn drie verschillende veel-op-veel-scenario's. Deze kunnen optreden wanneer u het volgende moet doen:
- Twee dimensietabellen koppelen
- twee feitentabellen koppelen
- Feitentabellen met een hogere korrel relateren, wanneer de feitentabel rijen opslaat met een hogere korrel dan de dimensietabelrijen
Veel-op-veel-dimensies relateren
Het klassieke veel-op-veel-scenario heeft betrekking op twee entiteiten, bijvoorbeeld bankklanten en bankrekeningen. Houd er rekening mee dat klanten meerdere accounts kunnen hebben en dat accounts meerdere klanten kunnen hebben. Wanneer een account meerdere klanten heeft, worden ze meestal gemeenschappelijke rekeninghoudersgenoemd.
Het modelleren van deze entiteiten is eenvoudig. Een dimensietabel accounts opslaat en een andere dimensietabel slaat klanten op. Zoals het kenmerk is van dimensietabellen, is er een unieke id-kolom (ID) in elke tabel. Als u de relatie tussen de twee tabellen wilt modelleren, is een derde tabel vereist. Deze tabel wordt meestal een overbruggingstabel genoemd. In dit voorbeeld is het doel om één rij op te slaan voor elke klantaccountkoppeling. Interessant: als deze tabel alleen id-kolommen bevat, wordt deze een feitloze feitentabelgenoemd.
Hier volgt een simplistisch diagram van de drie modeltabellen.
De eerste tabel heet Account
en bevat twee kolommen: AccountID
en Account
. De tweede tabel heet AccountCustomer
en bevat twee kolommen: AccountID
en CustomerID
. De derde tabel heeft de naam Customer
en bevat twee kolommen: CustomerID
en Customer
. Er bestaan geen relaties tussen een van de tabellen.
Er worden twee een-op-veel-relaties toegevoegd om de tabellen te relateren. Hier volgt een bijgewerkt modeldiagram van de gerelateerde tabellen. Er is een feitentabel met de naam Transaction
toegevoegd. Het registreert rekeningtransacties. De overbruggingstabel en alle identifier-kolommen zijn verborgen.
Om te beschrijven hoe de doorgifte van relatiefilters werkt, is het modeldiagram gewijzigd om de tabelrijen weer te geven.
De rijdetails voor de vier tabellen worden weergegeven in de volgende lijst met opsommingstekens:
- De tabel
Account
heeft twee rijen:-
AccountID
1 is voor Account-01 -
AccountID
2 is voor Account-02
-
- De tabel
Customer
heeft twee rijen:-
CustomerID
91 is voor Klant-91 -
CustomerID
92 is bedoeld voor Customer-92
-
- De tabel
AccountCustomer
heeft drie rijen:-
AccountID
1 is gekoppeld aanCustomerID
91 -
AccountID
1 is gekoppeld aanCustomerID
92 -
AccountID
2 is gekoppeld aanCustomerID
92
-
- De tabel
Transaction
heeft drie rijen:-
Date
1 januari 2019,AccountID
1,Amount
100 -
Date
2 februari 2019,AccountID
2,Amount
200 -
Date
3 maart 2019,AccountID
1,Amount
-25
-
Laten we eens kijken wat er gebeurt wanneer het model wordt opgevraagd.
In de volgende afbeelding zijn er twee tabelvisuals die de kolom Amount
van de Transaction
tabel samenvatten. De eerste visual groepeert per account, en dus de som van de Amount
kolommen vertegenwoordigt het rekeningsaldo. De tweede visual groepeert op klantniveau. Daarom weerspiegelt de som van de Amount
kolommen het klantsaldo.
De eerste tabelvisual (Rekeningsaldo) heeft twee kolommen: Account
en Amount
. Het volgende resultaat wordt weergegeven:
- rekening-01 saldobedrag is 75.
- rekening-02 saldo is 200.
- Het totaal is 275.
De tweede tabelweergave (Customer Balance) heeft twee kolommen: Customer
en Amount
. Het volgende resultaat wordt weergegeven:
- klant-91 saldobedrag is 275.
- klant-92 saldobedrag is 275.
- Het totaal is 275.
In een kort overzicht van de tabelrijen en de visual Account Balance ziet u dat het resultaat juist is, voor elke rekening en het totale bedrag. Dat komt doordat elke accountgroepering resulteert in een filteroverdracht naar de Transaction
-tabel voor dat account.
Er lijkt echter iets niet te kloppen met de visual Customer Balance. Elke klant in deze visual heeft hetzelfde saldo als het totale saldo. Dit resultaat kon alleen correct zijn als elke klant een gezamenlijke rekeninghouder van elke rekening was. Dat is niet het geval in dit voorbeeld. Er is een probleem en het is gerelateerd aan filterpropagatie. Filters stromen niet helemaal naar de Transaction
tabel.
Als u de filterrichtingen van de relatie vanuit de Customer
tabel naar de Transaction
tabel volgt, kunt u bepalen dat de relatie tussen de Account
en AccountCustomer
tabellen in de verkeerde richting wordt doorgegeven. De filterrichting voor deze relatie moet worden ingesteld op Both
.
Zoals verwacht, is er geen wijziging aangebracht in de visual Account Balance.
In de visual Customer Balance wordt nu echter het volgende resultaat weergegeven:
- bedrag van klant-91 is 75.
- klant-92 saldobedrag is 275.
- Het totaal is 275.
In de visual Customer Balance wordt nu een correct resultaat weergegeven. Volg zelf de filterinstructies en bekijk hoe de klantsaldi zijn berekend. Begrijp ook dat het visuele totaal van betrekking heeft op alle klanten.
Iemand die niet bekend is met de modelrelaties kan concluderen dat het resultaat onjuist is. Ze kunnen vragen: Waarom is het totale saldo voor Customer-91
en Customer-92
niet gelijk aan 350 (75 + 275)?
Het antwoord op hun vraag ligt in het begrijpen van de veel-op-veel-relatie. Elk klantsaldo kan de toevoeging van meerdere rekeningsaldi vertegenwoordigen, en daardoor zijn de klantsaldi niet te optellen.
Richtlijnen voor veel-op-veel-dimensies koppelen
Als u een veel-op-veel-relatie tussen dimensietabellen hebt, volgt u deze richtlijnen:
- Voeg elke veel-op-veel-gerelateerde entiteit toe als een modeltabel, zodat deze een id-kolom heeft.
- Voeg een overbruggingstabel toe om gekoppelde entiteiten op te slaan.
- Maak een-op-veel-relaties tussen de drie tabellen.
- Stel één bidirectionele relatie in om filteroverdracht voort te zetten naar de feiten-tabel.
- Als het niet geschikt is om ontbrekende id-waarden te hebben, schakelt u de eigenschap
Is Nullable
uit. Het vernieuwen van gegevens mislukt wanneer ontbrekende waarden worden opgehaald. - Verberg de overbruggingstabel (tenzij deze andere kolommen of metingen bevat die vereist zijn voor rapportage).
- Verberg alle id-kolommen die niet geschikt zijn voor rapportage (bijvoorbeeld wanneer in de kolommen surrogaatsleutelwaarden worden opgeslagen).
- Als het zinvol is om een ID-kolom zichtbaar te laten, moet u ervoor zorgen dat deze op de "een"-kant van de relatie staat—verberg altijd de kolom aan de "veel"-kant. Dat komt doordat filters die zijn toegepast op de eerste dia resulteren in betere filterprestaties.
- Om verwarring of onjuiste interpretatie te voorkomen, communiceert u uitleg aan uw rapportgebruikers. U kunt beschrijvingen toevoegen met tekstvakken of knopinfo voor visuele kopteksten.
Het is niet raadzaam om veel-op-veel dimensietabellen rechtstreeks te koppelen. Deze ontwerpbenadering vereist het instellen van een relatie met een veel-op-veel-kardinaliteit. Conceptueel gezien kan dit worden bereikt, maar het impliceert dat de gerelateerde kolommen dubbele waarden kunnen bevatten. Het is een goed geaccepteerde ontwerppraktijk dat dimensietabellen een id-kolom hebben. Dimensietabellen moeten altijd de id-kolom gebruiken als de 'een'-zijde van een relatie.
Veel-op-veel-feiten relateren
Een ander veel-op-veel-scenariotype omvat het koppelen van twee feitentabellen. Twee feitentabellen kunnen rechtstreeks worden gerelateerd. Deze ontwerptechniek kan handig zijn voor snelle en eenvoudige gegevensverkenning. Maar om duidelijk te zijn, raden we deze ontwerpbenadering over het algemeen niet aan. Verderop in deze sectie wordt uitgelegd waarom.
Laten we eens kijken naar een voorbeeld met twee feitentabellen: Order
en Fulfillment
. De Order
tabel bevat één rij per orderregel en de Fulfillment
tabel kan nul of meer rijen per orderregel bevatten. Rijen in de tabel Order
vertegenwoordigen verkooporders. Rijen in de tabel Fulfillment
vertegenwoordigen orderitems die zijn verzonden. Een veel-op-veel-relatie betreft de OrderID
kolommen in elke tabel, waarbij de filters alleen worden doorgegeven vanuit de Order
tabel (wat betekent dat de Order
tabel de Fulfillment
tabel filtert).
De relatiekardinaliteit is ingesteld op Many-to-many
ter ondersteuning van het opslaan van dubbele OrderID
kolomwaarden in beide tabellen. In de Order
tabel kunnen dubbele id-waarden bestaan omdat een order meerdere regels kan bevatten. In de Fulfillment
tabel kunnen dubbele id-waarden bestaan omdat orders meerdere regels kunnen hebben en orderregels kunnen worden uitgevoerd door veel zendingen.
Laten we nu eens kijken naar de rijen van de tabel. In de tabel Fulfillment
ziet u dat orderregels kunnen worden vervuld door meerdere zendingen. (Het ontbreken van een orderregel betekent dat de bestelling nog moet worden uitgevoerd.)
De details van de rijen voor de twee tabellen worden beschreven in de volgende opsomming:
- De tabel
Order
heeft vijf rijen:-
OrderDate
1 januari 2019,OrderID
1,OrderLine
1,ProductID
Prod-A,OrderQuantity
5,Sales
50 -
OrderDate
1 januari 2019,OrderID
1,OrderLine
2,ProductID
Prod-B,OrderQuantity
10,Sales
80 -
OrderDate
2 februari 2019,OrderID
2,OrderLine
1,ProductID
Prod-B,OrderQuantity
5,Sales
40 -
OrderDate
2 februari 2019,OrderID
2,OrderLine
2,ProductID
Prod-C,OrderQuantity
1,Sales
20 -
OrderDate
3 maart 2019,OrderID
3,OrderLine
1,ProductID
Prod-C,OrderQuantity
5,Sales
100
-
- De tabel
Fulfillment
heeft vier rijen:-
FulfillmentDate
1 januari 2019,FulfillmentID
50,OrderID
1,OrderLine
1,FulfillmentQuantity
2 -
FulfillmentDate
2 februari 2019,FulfillmentID
51,OrderID
2,OrderLine
1,FulfillmentQuantity
5 -
FulfillmentDate
2 februari 2019,FulfillmentID
52,OrderID
1,OrderLine
1,FulfillmentQuantity
3 -
FulfillmentDate
1 januari 2019,FulfillmentID
53,OrderID
1,OrderLine
2,FulfillmentQuantity
10
-
Laten we eens kijken wat er gebeurt wanneer het model wordt opgevraagd. Hier is een tabelvisualisatie die de hoeveelheden van bestellingen en uitvoering vergelijkt met behulp van de Order
tabel OrderID
kolom.
De visual geeft een nauwkeurig resultaat weer. Het nut van het model is echter beperkt omdat u alleen kunt filteren of groeperen op de Order
tabel OrderID
kolom.
Richtlijnen voor veel-op-veel-feiten relateren
Over het algemeen raden we u niet aan om twee feitentabellen rechtstreeks met elkaar te verbinden door gebruik te maken van veel-op-veel-kardinaliteit. De belangrijkste reden hiervoor is dat het model geen flexibiliteit biedt op de manieren waarop uw rapportvisuals filteren of groeperen. In het voorbeeld is het alleen mogelijk om visuals te filteren of te groeperen met kolom OrderID
van tabel Order
. Een andere reden heeft betrekking op de kwaliteit van uw gegevens. Als uw gegevens integriteitsproblemen hebben, kunnen sommige rijen worden weggelaten tijdens het uitvoeren van queries vanwege de aard van de veel-op-veel kardinaliteit en beperkte relaties.
In plaats van feitentabellen rechtstreeks te koppelen, raden we u aan een stervormig schema te implementeren ontwerp. Dat betekent dat u dimensietabellen toevoegt. Deze dimensietabellen hebben vervolgens betrekking op de feitentabellen met behulp van een-op-veel-relaties. Deze ontwerpbenadering is robuust omdat deze efficiënt flexibele rapportageopties biedt. Hiermee kunt u filteren of groeperen met behulp van een van de dimensietabelkolommen en kolommen van een gerelateerde feitentabel samenvatten.
Laten we eens kijken naar een betere oplossing.
Let op de volgende ontwerpwijzigingen:
- Het model heeft nu vier extra tabellen:
OrderLine
,OrderDate
,Product
enFulfillmentDate
. - De vier extra tabellen zijn alle dimensietabellen waarbij een-op-veel-relaties deze relateren aan de feitentabellen.
- De
OrderLine
tabel bevat de kolomOrderLineID
, waarin deOrderID
waarde wordt opgeslagen, vermenigvuldigd met 100, plus deOrderLine
kolomwaarde, een id voor elke orderregel. - De tabellen
Order
enFulfillment
bevatten nu elk eenOrderLineID
kolom en bevatten niet langer de kolommenOrderID
enOrderLine
. - De tabel
Fulfillment
bevat nuOrderDate
enProductID
kolommen. - De tabel
FulfillmentDate
heeft alleen een relatie met deFulfillment
tabel. - Alle id-kolommen zijn verborgen.
Het nemen van de tijd om een stervormig schemaontwerp te gebruiken biedt de volgende voordelen:
- Uw rapportvisuals kunnen filteren of groeperen op een zichtbare kolom uit de dimensietabellen.
- Uw rapportvisuals kunnen elke zichtbare kolom uit de feitentabellen samenvatten.
- Filters die zijn toegepast op de
OrderLine
,OrderDate
ofProduct
tabellen worden doorgegeven aan beide feitentabellen. - Alle relaties zijn een-op-veel en elke relatie is een reguliere relatie. Problemen met gegevensintegriteit worden niet gemaskeerd. Zie Modelrelaties in Power BI Desktopvoor meer informatie over de evaluatie van relaties.
Feiten over graan van hogere kwaliteit relateren
Dit veel-op-veel-scenario verschilt van de andere twee die al in dit artikel zijn beschreven.
Laten we eens kijken naar een voorbeeld met vier tabellen: Date
, Sales
, Product
en Target
. De Date
- en Product
tabellen zijn dimensietabellen en een-op-veel-relaties hebben betrekking op de Sales
feitentabel. Tot nu toe vertegenwoordigt het een goed stervormig schemaontwerp. De Target
tabel is echter nog niet gerelateerd aan de andere tabellen.
De tabel Target
bevat drie kolommen: Category
, TargetQuantity
en TargetYear
. In de tabelrijen wordt een granulariteit van jaar en productcategorie weergegeven. Met andere woorden, doelen, die worden gebruikt om de verkoopprestaties te meten, worden elk jaar voor elke productcategorie ingesteld.
Omdat de Target
tabel gegevens op een hoger niveau opslaat dan de dimensietabellen, kan er geen een-op-veel-relatie worden gemaakt. Nou, het is waar voor slechts één van de relaties. Laten we eens kijken hoe de Target
tabel kan worden gerelateerd aan de dimensietabellen.
Hogere tijdsperioden relateren
Een relatie tussen de Date
en Target
tabellen moet een een-op-veel-relatie zijn. Dat komt doordat de TargetYear
kolomwaarden datums zijn. In dit voorbeeld wordt in elke kolom TargetYear
de eerste datum van het doeljaar opgeslagen.
Tip
Als u feiten met een hogere tijdgranulariteit dan dag opslaat, stelt u het gegevenstype van de kolom in op Datum (of Geheel getal als u datumsleutels gebruikt). Sla in de kolom een waarde op die de eerste dag van de periode aangeeft. Een jaarperiode wordt bijvoorbeeld geregistreerd als 1 januari van het jaar en een maandperiode wordt geregistreerd als de eerste dag van die maand.
Zorg er echter voor dat filters op maand- of datumniveau een zinvol resultaat opleveren. Zonder speciale berekeningslogica kunnen rapportvisuals rapporteren dat doeldatums letterlijk de eerste dag van elk jaar zijn. Alle andere dagen, en alle maanden behalve januari, geven een overzicht van de doelhoeveelheid als BLANK.
In de volgende matrix-visual ziet u wat er gebeurt wanneer de rapportgebruiker inzoomt van een jaar naar de maanden. De visual bevat een overzicht van de kolom TargetQuantity
. (De optie Items zonder gegevens weergeven is ingeschakeld voor de matrixrijen.)
Om dit gedrag te voorkomen, raden we u aan de samenvatting van uw feitengegevens te beheren met behulp van metingen. Een manier om de samenvatting te beheren, is door BLANK te retourneren wanneer er query's worden uitgevoerd op perioden op een lager niveau. Een andere manier, gedefinieerd met een aantal geavanceerde DAX, is het toewijzen van waarden voor perioden op een lager niveau.
Houd rekening met de volgende metingsdefinitie die gebruikmaakt van de ISFILTERED DAX-functie. Er wordt alleen een waarde geretourneerd wanneer de Date
en Month
kolommen niet worden gefilterd.
Target Quantity =
IF(
NOT ISFILTERED('Date'[Date])
&& NOT ISFILTERED('Date'[Month]),
SUM(Target[TargetQuantity])
)
In de volgende matrixvisualisatie wordt de meting Target Quantity
gebruikt. Hier ziet u dat alle maandelijkse doelhoeveelheden BLANK zijn.
Hogere korrel (niet-datum) relateren
Er is een andere ontwerpbenadering vereist bij het koppelen van een kolom zonder datum van een dimensietabel aan een feitentabel (en deze heeft een grotere detaillering dan de dimensietabel).
De Category
kolommen (uit zowel de Product
- als Target
tabellen) bevatten dubbele waarden. Er is dus geen ene kant voor een een-op-veel-relatie. In dit geval moet u een veel-op-veel-relatie maken. De relatie moet filters in één richting doorgeven, van de dimensietabel naar de feitentabel.
Laten we nu eens kijken naar de tabelrijen.
In de tabel Target
zijn er vier rijen: twee rijen voor elk doeljaar (2019 en 2020) en twee categorieën (Kleding en Accessoires). In de tabel Product
zijn er drie producten. Twee behoren tot de kledingcategorie en één behoort tot de categorie accessoires. Een van de kledingkleuren is groen en de resterende twee zijn blauw.
Een tabelvisualisatie die de Category
-kolom groepeert uit de Product
-tabel produceert het volgende resultaat. Deze visual produceert echter het juiste resultaat. Laten we nu eens kijken wat er gebeurt wanneer de kolom Color
uit de Product
tabel wordt gebruikt om de doelhoeveelheid te groeperen.
De visual produceert een onjuiste voorstelling van de gegevens. Wat gebeurt er hier?
Een filter op de kolom Color
uit de Product
tabel resulteert in twee rijen. Een van de rijen is voor de categorie Kleding en de andere is voor de categorie Accessoires. Deze twee categoriewaarden worden doorgegeven als filters aan de Target
tabel. Met andere woorden, omdat de kleurblauw wordt gebruikt door producten uit twee categorieën, deze categorieën worden gebruikt om de doelen te filteren.
Om dit gedrag te voorkomen, zoals eerder beschreven, raden we u aan de samenvatting van uw feitengegevens te beheren met behulp van metingen.
Houd rekening met de volgende metingsdefinitie. U ziet dat alle Product
tabelkolommen onder het categorieniveau worden getest op filters.
Target Quantity =
IF(
NOT ISFILTERED('Product'[ProductID])
&& NOT ISFILTERED('Product'[Product])
&& NOT ISFILTERED('Product'[Color]),
SUM(Target[TargetQuantity])
)
In de volgende tabelvisual wordt de maatstaf Target Quantity
gebruikt. U ziet dat alle kleurdoelhoeveelheden BLANK zijn.
Het uiteindelijke modelontwerp ziet er als volgt uit.
Richtlijnen voor het relateren van meer gedetailleerde feiten
Als u een dimensietabel wilt koppelen aan een feitentabel en de feitentabel rijen opslaat met een hogere korrel dan de dimensietabelrijen, volgt u deze richtlijnen:
-
Voor datums van hogere feiten
- Sla in de feitentabel de eerste datum van de periode op.
- Maak een een-op-veel-relatie tussen de datumtabel en de feitentabel.
-
Voor andere feiten met een hogere graan
- Maak een veel-op-veel-relatie tussen de dimensietabel en de feitentabel.
-
Voor beide typen
- Beheer samenvatting met meetlogica—retourneer BLANK wanneer lagere-dimensiekolommen worden gebruikt om te filteren of te groeperen.
- Verberg samenvattende feitentabelkolommen, zodat alleen metingen kunnen worden gebruikt om de feitentabellen samen te vatten.
Verwante inhoud
Raadpleeg de volgende bronnen voor meer informatie over dit artikel: