多対多のリレーションシップに関するガイダンス
この記事では、Power BI Desktop を使用するデータ モデリング担当者を対象にしています。 ここでは、3 つの異なる多対多モデリング シナリオについて説明します。 また、モデルで正しく設計する方法についてのガイダンスも提供します。
手記
モデル リレーションシップの概要については、この記事では説明しません。 リレーションシップ、それらのプロパティ、またはそれらを構成する方法について完全に理解していない場合は、最初に Power BI Desktop の
また、スター スキーマの設計について理解していることも重要です。 詳細については、「スター スキーマと Power BIの重要性」を参照してください。
3 つの異なる多対多シナリオがあります。 これらは、次の必要があるときに発生する可能性があります。
- 2 つのディメンション テーブルを関連付
- 2 つのファクト テーブルを関連付
- ファクト テーブルがディメンジョン テーブルの行よりも高い粒度で行を格納する場合に、より粒度の高いファクト テーブルを関連付ける
多対多のディメンションを関連付ける
従来の多対多シナリオでは、銀行の顧客や銀行口座などの 2 つのエンティティが関連付けられます。 顧客は複数のアカウントを持ち、アカウントには複数の顧客を持つことができます。 1 つのアカウントに複数の顧客がある場合、一般的に共同アカウント所有者 呼ばれます。
これらのエンティティのモデル化は簡単です。 1 つの ディメンション テーブル アカウントが格納され、もう 1 つのディメンション テーブルに顧客が格納されます。 ディメンション テーブルに共通する特性として、各テーブルには一意の識別子 (ID) 列があります。 2 つのテーブル間のリレーションシップをモデル化するには、3 番目のテーブルが必要です。 このテーブルは、一般に、ブリッジ テーブルと呼ばれます。 この例では、顧客とアカウントの関連付けごとに 1 つの行を格納することが目的です。 興味深いことに、このテーブルに識別子列のみが含まれている場合は、
3 つのモデル テーブルの単純な図を次に示します。
最初のテーブルは Account
という名前で、AccountID
と Account
の 2 つの列が含まれています。 2 番目のテーブルは AccountCustomer
という名前で、AccountID
と CustomerID
の 2 つの列が含まれています。 3 番目のテーブルは Customer
という名前で、CustomerID
と Customer
の 2 つの列が含まれています。 リレーションシップは、どのテーブルにも存在しません。
テーブルを関連付けるために、2 つの一対多リレーションシップが追加されます。 関連テーブルの更新されたモデル図を次に示します。 Transaction
という名前のファクト テーブルが追加されました。 アカウント トランザクションを記録します。 ブリッジ テーブルとすべての識別子列が非表示になっています。
リレーションシップ フィルターの伝達のしくみを説明するために、モデル ダイアグラムがテーブル行を表示するように変更されました。
次の箇条書きには、4 つのテーブルの行の詳細が表示されます。
Account
テーブルには、次の 2 つの行があります。AccountID
1 は Account-01 用ですAccountID
2 は Account-02 用です
Customer
テーブルには、次の 2 つの行があります。CustomerID
91 は、Customer-91 用ですCustomerID
92 は、Customer-92 用です
AccountCustomer
テーブルには、次の 3 つの行があります。AccountID
1 は、CustomerID
91 に関連付けられています。AccountID
1 はCustomerID
92 に関連付けられていますAccountID
2 は、CustomerID
92 に関連付けられています。
Transaction
テーブルには、次の 3 つの行があります。Date
2019 年 1 月 1 日,AccountID
1,Amount
100Date
2019 年 2 月 2 日,AccountID
2,Amount
200Date
2019 年 3 月 3 日,AccountID
1,Amount
-25
モデルへの問い合わせで何が起こるかを見てみましょう。
次の図では、Transaction
テーブルの Amount
列を要約した 2 つのテーブル ビジュアルがあります。 最初のビジュアルはアカウントごとにグループ化されるため、Amount
列の合計は、アカウント残高を表します。 2 番目のビジュアルは顧客別にグループ化されるため、
最初のテーブル ビジュアル (勘定科目残高) には、Account
と Amount
の 2 つの列があります。 次の結果が表示されます。
- 口座-01の 残高金額は75です。
- 口座-02 残高は 200。
- 合計は 275です。
2 番目のテーブル ビジュアル (Customer Balance) には、Customer
と Amount
の 2 つの列があります。 次の結果が表示されます。
- Customer-91 残高金額は 275です。
- Customer-92 の残高は 275です。
- 合計は 275です。
テーブルの行とアカウント残高のビジュアルを一目で見ると、各勘定と合計金額の結果が正しいことを確認できます。 これは、アカウントをグループ化するたびに、そのアカウントの Transaction
テーブルへのフィルター伝達が行われるためです。
ただし、Customer Balance ビジュアルでは何かが正しく表示されません。 このビジュアルの各顧客は、合計残高と同じ残高を持ちます。 この結果は、すべての顧客がすべてのアカウントの共同アカウント所有者である場合にのみ正しい可能性があります。 この例ではそうではありません。 問題があり、フィルターの伝達に関連しています。 フィルターは、Transaction
テーブルまで流れるわけではありません。
Customer
テーブルから Transaction
テーブルへのリレーションシップ フィルターの方向に従うと、Account
テーブルと AccountCustomer
テーブル間のリレーションシップが間違った方向に伝達されていることを確認できます。 このリレーションシップのフィルターの方向を Both
に設定する必要があります。
予想どおり、[アカウント残高] ビジュアルに変更はありません。
ただし、Customer Balance ビジュアルには次の結果が表示されます。
- Customer-91 の残高は 75 です。
- Customer-92 の残高は 275 です。
- 合計は 275です。
Customer Balance ビジュアルに正しい結果が表示されるようになりました。 自分のフィルターの指示に従って、顧客の残高がどのように計算されたかを確認します。 また、視覚化の合計が "すべての顧客" を意味していることを理解してください。
モデルの関係に慣れていない人は、結果が正しくないと結論付けることができます。 Customer-91
と Customer-92
の合計残高が 350 (75 + 275) に等しくないのはなぜですか。
彼らの質問に対する答えは、多対多の関係を理解することです。 各顧客残高は複数の口座残高の加算を表すことができるため、顧客の残高は加法ではありません 。
多対多ディメンションの関連付けに関するガイダンス
ディメンション テーブル間に多対多リレーションシップがある場合は、次のガイダンスに従います。
- 各多対多関連エンティティをモデル テーブルとして追加し、ID 列があることを確認します。
- 関連付けられたエンティティを格納するブリッジ テーブルを追加します。
- 3 つのテーブル間に一対多リレーションシップを作成します。
- "1 つ" の双方向リレーションシップを設定して、フィルターの伝達をファクト テーブルに続行できるようにします。
- 不足している ID 値を持つことが適切でない場合は、
Is Nullable
プロパティを無効にします。欠損値が取得されると、データの更新は失敗します。 - ブリッジ テーブルを非表示にします (レポートに必要な他の列またはメジャーが含まれていない場合)。
- レポートに適さない ID 列を非表示にします (たとえば、列にサロゲート キー値が格納されている場合)。
- ID 列を表示したままにするのが理にかなっている場合は、リレーションシップの "一" 側にあることを確認します。"多" 側の列は常に非表示にします。 これは、"1" スライドにフィルターを適用すると、フィルターのパフォーマンスが向上するためです。
- 混乱や解釈の誤りを回避するには、レポート ユーザーに説明を伝えます。テキスト ボックスや視覚的なヘッダー ツールを使用して説明を追加できます。
多対多ディメンション テーブルを直接関連付けすることはお勧めしません。 この設計アプローチでは、多対多カーディナリティとのリレーションシップを設定する必要があります。 概念的には達成できますが、関連する列に重複する値が含まれている可能性があることを意味します。 ただし、これはよく受け入れられる設計手法ですが、ディメンション テーブルには ID 列があります。 ディメンション テーブルでは、リレーションシップの "一" 側として ID 列を常に使用する必要があります。
多対多ファクトを関連付ける
異なる多対多シナリオの種類には、2 つのファクト テーブルを関連付ける必要があります。 2 つのファクト テーブルを直接関連付けることができます。 この設計手法は、迅速かつ簡単なデータ探索に役立ちます。 ただし、明確にするために、通常、この設計アプローチはお勧めしません。 その理由については、このセクションの後半で説明します。
Order
と Fulfillment
の 2 つのファクト テーブルを含む例を考えてみましょう。 Order
テーブルには注文明細行ごとに 1 行が含まれており、Fulfillment
テーブルには注文明細行ごとに 0 行以上の行を含めることができます。 Order
テーブルの行は販売注文を表します。 Fulfillment
テーブルの行は、出荷された注文品目を表します。 多対多リレーションシップは、各テーブルの OrderID
列と、Order
テーブルからのみフィルター伝達を関連付けます (つまり、Order
テーブルは Fulfillment
テーブルをフィルター処理します)。
リレーションシップのカーディナリティが Many-to-many
に設定され、両方のテーブルに重複する OrderID
列の値を格納できます。 Order
テーブルには、注文に複数の行が含まれる可能性があるため、重複する ID 値が存在する可能性があります。 Fulfillment
テーブルでは、注文に複数の行を含めることができるため、重複する ID 値が存在する可能性があり、注文明細行は多数の出荷で満たすことができます。
次は、テーブル行を見てみましょう。 Fulfillment
テーブルでは、注文行が複数の出荷のフルフィルメントの対象となることがあることに注意してください。 (注文明細行がない場合は、注文がまだ満たされていないことを意味します)。
2 つのテーブルの行の詳細については、次の箇条書きで説明します。
Order
テーブルには、次の 5 つの行があります。OrderDate
2019 年 1 月 1 日,OrderID
1,OrderLine
1,ProductID
Prod-A,OrderQuantity
5,Sales
50OrderDate
2019 年 1 月 1 日,OrderID
1,OrderLine
2,ProductID
Prod-B,OrderQuantity
10,Sales
80OrderDate
2019 年 2 月 2 日,OrderID
2,OrderLine
1,ProductID
Prod-B,OrderQuantity
5,Sales
40OrderDate
2019 年 2 月 2 日,OrderID
2,OrderLine
2,ProductID
Prod-C,OrderQuantity
1,Sales
20OrderDate
2019 年 3 月 3 日,OrderID
3,OrderLine
1,ProductID
Prod-C,OrderQuantity
5,Sales
100
Fulfillment
テーブルには、次の 4 つの行があります。FulfillmentDate
2019 年 1 月 1 日,FulfillmentID
50,OrderID
1,OrderLine
1,FulfillmentQuantity
2FulfillmentDate
2019 年 2 月 2 日,FulfillmentID
51,OrderID
2,OrderLine
1,FulfillmentQuantity
5FulfillmentDate
2019 年 2 月 2 日,FulfillmentID
52,OrderID
1,OrderLine
1,FulfillmentQuantity
3FulfillmentDate
2019 年 1 月 1 日,FulfillmentID
53,OrderID
1,OrderLine
2,FulfillmentQuantity
10
モデルにクエリを実行するとどうなるかを見てみましょう。 次に示すのは、Order
テーブルの OrderID
列ごとに注文とフルフィルメントの数量を比較するテーブルの視覚エフェクトです。
ビジュアルは正確な結果を示します。 ただし、Order
テーブル OrderID
列によってのみフィルター処理またはグループ化できるため、モデルの有用性は制限されます。
多対多ファクトの関連付けに関するガイダンス
一般に、多対多カーディナリティを使用して 2 つのファクト テーブルを直接関連付けすることはお勧めしません。 主な理由は、レポート ビジュアルのフィルター処理やグループ化の方法がモデルによって柔軟に提供されないためです。 この例では、ビジュアルが Order
テーブル OrderID
列でフィルター処理またはグループ化することだけが可能です。 もう 1 つの理由は、データの品質に関連しています。 データに整合性の問題がある場合は、多対多のカーディナリティと制限付きリレーションシップの特質によりクエリ中に一部の行が省略される可能性があります。
ファクト テーブルを直接関連付けるのではなく、スター スキーマ 設計を実装することをお勧めします。 つまり、ディメンション テーブルを追加します。 次に、これらのディメンション テーブルは、一対多リレーションシップを使用してファクト テーブルに関連付けます。 この設計アプローチは、柔軟なレポート オプションを効率的に提供するために堅牢です。 ディメンション テーブル列を使用してフィルター処理またはグループ化したり、関連するファクト テーブルの列を集計したりできます。
より良い解決策を考えてみましょう。
次の設計変更に注意してください。
- モデルには、
OrderLine
、OrderDate
、Product
、FulfillmentDate
の 4 つのテーブルが追加されました。 - 4 つの追加テーブルは、一対多リレーションシップがファクト テーブルに関連付けられているすべてのディメンション テーブルです。
OrderLine
テーブルには、OrderLineID
列が含まれています。この列には、OrderID
値に 100 を乗算した値と、OrderLine
列の値 (各注文明細行の ID) が格納されます。Order
テーブルとFulfillment
テーブルには、それぞれOrderLineID
列が含まれるようになり、OrderID
列とOrderLine
列は含めなくなりました。Fulfillment
テーブルにOrderDate
列とProductID
列が含まれるようになりました。FulfillmentDate
テーブルには、Fulfillment
テーブルにのみリレーションシップがあります。- すべての ID 列は非表示になります。
スター スキーマ設計の採用に時間がかかると、次の利点があります。
- レポート ビジュアルは、ディメンション テーブルから表示される任意の列で "フィルター処理またはグループ化" できます。
- レポート ビジュアルは、ファクト テーブルから表示される列を "集計" できます。
OrderLine
、OrderDate
、またはProduct
テーブルに適用されるフィルターは、両方のファクト テーブルに反映されます。- すべてのリレーションシップは一対多であり、各リレーションシップは
通常のリレーションシップです。 データ整合性の問題はマスクされません。 リレーションシップの評価の詳細については、「Power BI Desktopでのモデルリレーションシップ」を参照してください。
より高い粒度のファクトを関連付ける
この多対多シナリオは、この記事で既に説明されている他の 2 つのシナリオとは大きく異なります。
Date
、Sales
、Product
、Target
の 4 つのテーブルを含む例を考えてみましょう。 Date
テーブルと Product
テーブルはディメンション テーブルであり、一対多リレーションシップはそれぞれ Sales
ファクト テーブルに関連付けられます。 今のところ、適切なスター スキーマ設計が示されています。 ただし、Target
テーブルは、他のテーブルとまだ関連していません。
Target
テーブルには、Category
、TargetQuantity
、および TargetYear
の 3 つの列があります。 テーブルの行には、年と製品カテゴリの詳細が表示されます。 つまり、販売実績の測定に使用されるターゲットは、製品カテゴリごとに毎年設定されます。
Target
テーブルにはディメンション テーブルよりも高いレベルでデータが格納されるため、一対多リレーションシップを作成することはできません。 さて、それは関係の1つだけに当てはまります。 Target
テーブルをディメンション テーブルに関連付ける方法を見てみましょう。
より高い粒度の期間を関連付ける
Date
テーブルと Target
テーブル間のリレーションシップは、一対多リレーションシップである必要があります。 これは、TargetYear
列の値が日付であるためです。 この例では、各 TargetYear
列にターゲット年の最初の日付が格納されます。
ヒント
日よりも高い時間粒度でファクトを格納する場合は、列のデータ型を
ただし、月または日付レベルのフィルターによって意味のある結果が生成されるように注意する必要があります。 特別な計算ロジックがない場合、レポート ビジュアルでは、ターゲットの日付が実際には各年の最初の日であると報告される場合があります。 それ以外のすべての日 (および 1 月を除くすべての月) は、目標数量を空白として集計します。
次のマトリックス ビジュアルは、レポート ユーザーが年から月にドリルダウンした場合の動作を示しています。 ビジュアルは、TargetQuantity
列を要約します。 (マトリックス行に対して[データ のない項目を表示する]オプションが有効になっています。
この動作を避けるには、指標を使用してファクトデータの集計を制御することをお勧めします。 集計を制御する 1 つの方法は、下位レベルの期間が照会されたときに BLANK を返す方法です。 高度な DAX で定義されるもう 1 つの方法は、低レベルの期間にわたって値を割り当てすることです。
ISFILTERED DAX 関数を使用する次のメジャー定義について考慮します。 Date
列と Month
列がフィルター処理されていない場合にのみ、値が返されます。
Target Quantity =
IF(
NOT ISFILTERED('Date'[Date])
&& NOT ISFILTERED('Date'[Month]),
SUM(Target[TargetQuantity])
)
次の行列ビジュアルでは、Target Quantity
指標を使用します。 すべての月次目標数量が空白であることを示します。
より高い粒度を関連付ける (日付以外)
ディメンション テーブルからファクト テーブルに日付以外の列を関連付ける場合は、別の設計方法が必要です (ディメンション テーブルよりも高い粒度です)。
(Product
テーブルと Target
テーブルの両方の) Category
列に重複する値が含まれています。 そのため、一対多のリレーションシップに対して「その一方」の側はありません。 この場合は、多対多リレーションシップを作成する必要があります。 リレーションシップは、ディメンション テーブルからファクト テーブルに対して、フィルターを 1 方向に伝達する必要があります。
次は、テーブル行を見てみましょう。
Target
テーブルには、4 つの行があります。目標年ごとに 2 行 (2019 年と 2020 年)、2 つのカテゴリ (衣料品とアクセサリ) です。 Product
テーブルには、3 つの製品があります。 2 つは衣料品カテゴリに属し、1 つはアクセサリ カテゴリに属します。 服の色の 1 つは緑色で、残りの 2 つは青です。
Product
テーブルの Category
列でグループ化されたテーブルビジュアルでは、次の結果が生成されます。 ただし、このビジュアルでは正しい結果が生成されます。 次に、Product
テーブルの Color
列を使用してターゲット数量をグループ化するとどうなるかを考えてみましょう。
ビジュアルによって、データが誤って表示されます。 ここで何が起こっていますか?
Product
テーブルの Color
列に対するフィルターは、2 つの行になります。 行の 1 つは [服] カテゴリ用で、もう 1 つは [アクセサリ] カテゴリ用です。 これら 2 つのカテゴリ値は、フィルターとして Target
テーブルに反映されます。 つまり、青色は 2 つのカテゴリの製品で使用されるため、ターゲットのフィルター処理には "それらのカテゴリ" が使用されます。
前述のように、この現象を回避するには、指標を使用してファクトデータの集約を制御することをお勧めします。
次の測定定義について考えてみましょう。 カテゴリ レベルの下にあるすべてのテーブル列 Product
フィルターがテストされていることに注意してください。
Target Quantity =
IF(
NOT ISFILTERED('Product'[ProductID])
&& NOT ISFILTERED('Product'[Product])
&& NOT ISFILTERED('Product'[Color]),
SUM(Target[TargetQuantity])
)
次の表の視覚エフェクトでは、Target Quantity
メジャーを使用します。 すべての色ターゲット数量が空白であることを示します。
最終的なモデル設計は次のようになります。
より高い粒度のファクトの関連付けに関するガイダンス
ディメンション テーブルをファクト テーブルに関連付ける必要があり、ファクト テーブルがディメンション テーブルの行よりも高い粒度で行を格納する場合は、次のガイダンスに従います。
- より粒度の高いファクト日付の場合
- ファクト テーブルに、期間の最初の日付を格納します。
- 日付テーブルとファクト テーブルの間に一対多リレーションシップを作成します。
- その他のより粒度の高いファクトの場合
- ディメンション テーブルとファクト テーブルの間に多対多リレーションシップを作成します。
- 両方の種類の場合
- メジャー ロジックを使用して集計を制御します。下位レベルのディメンション列を使用してフィルターまたはグループ化する場合は、BLANK が返されます。
- 集計可能なファクト テーブル列を非表示にします。これにより、メジャーのみを使用してファクト テーブルを集計できます。
関連コンテンツ
この記事に関連する詳細については、次のリソースを参照してください。
- Power BI Desktop でのモデル リレーションシップ
- スター スキーマと Power BI の重要性を理解する
- リレーションシップのトラブルシューティング ガイダンス
- 問。 Fabric コミュニティに尋ねてみてください
- 提案はありますか? Fabric の を向上させるためにアイデアを投稿する