keyboard_arrow_up
ホームツール

Tableauにおけるデータ結合とは

Tableauにおけるデータ結合とは

こんにちは、minoRiです。

私は現在、Tableauを活用できるようになるために、日々学習をしています。

先日、Tableau Certified Data Analystに合格し、実践的なデータ分析に取り組んでいるのですが、「データ結合」の機能を利用するところで躓いてしまいました。そこで「データ結合」がどのようなものなのかに絞って再度勉強し、理解を深めました。

今回は、私が「データ結合」について理解するために勉強したことをまとめます。私のように「データ結合」に躓いてしまった方の助けになれば嬉しいです。

この記事では、「データ結合とはどんな操作なのか」を実践例を用いて解説していきます。

文章で説明されるだけでは理解が難しい部分もあると思い、手元で操作ができるようにし、図解しました。1つずつ確認しながら「なぜそうなるのか?」を一緒に紐解いていきましょう。

※記事で利用するサンプルデータのダウンロードリンク

下記リンクは、この記事内で扱うECショップのサンプルデータ(ChatGPTで作成)のダウンロードリンクです。理解を深めるためにも、自分の手元で同じ操作をしながら読み進めることをおすすめします。

サンプルデータのダウンロードリンク


データ結合とは?

定義

2つ以上のテーブル(表)を共通項目(結合句)でつなぎ、1つのテーブルにする操作のことです。

「データを横につなぐ」と表現されることもあります。

…と定義を言葉だけで示されていることが多いのですが、私には難しかったため、具体例と図を用いて解説します。

具体例

【ECショップの顧客情報とランク情報からGold会員へメールを送りたい場合】

使用するテーブル:

①”customer”テーブル(顧客情報データ)… 顧客ID、氏名、居住市、Eメール、電話番号、年齢

”customer”テーブルの表データ

②”loyalty”テーブル(顧客のランクデータ)… 顧客ID、ランク、入会日、保有ポイント

”loyalty”テーブルの表データ

この2つのテーブルを共通項目である「顧客ID」を結合句として基準にし、対応する項目をつなげて1つのテーブルにします。

この操作がどのようなものかを、表を使って確認してみましょう。

まず、”customer”テーブルと”loyalty”テーブルがあります

”customer”テーブルと”loyalty”テーブルのExcelの表を並べた図

これら2つのテーブルを「顧客ID」が対応するように、横につなぎます。

”customer”テーブルと”loyalty”テーブルを結合した結果のTableauの表

以上のように顧客ID、氏名、居住市、Eメール、電話番号、年齢、ランク、入会日、保有ポイントという情報が横並びにつながり、1つのテーブルとなります。

これが「結合」という操作です。

今回の目的であるGold会員へメールを送るためには、結合後にGold会員に絞り込むことで、送信対象者の氏名とEメールを抽出できます。

このように、結合とは「2つ以上のテーブル(表)を共通項目(結合句)で横につないで1つのテーブルにすること」です。

今回の例では、”customer”テーブルと”loyalty”テーブルを「顧客ID」で横につないで、1つのテーブルにしています。

結合の方法には種類があるので、次のセクションでそれぞれについて解説していきます。


結合の種類

結合には4つの種類があり、それぞれ下の画像のようなベン図で表現されます。

ベン図で可視化した4種類の結合

このセクションでも、冒頭でダウンロードリンクを貼ったECショップのサンプルデータを使って、各結合について見ていきます。使用するテーブルは、”customer”(顧客情報)テーブルと”orders”(注文情報)テーブルの2つです。

”customer”テーブルと”orders”テーブルの各表

内部結合(INNER JOIN)

定義:2つのテーブルの共通項目について、結合句の文字列もしくは値が一致している(条件に合う)行だけを残す結合方法です。片方にしかないデータは残りません。

具体例:
”customer”テーブルと”orders”テーブルで「顧客ID」を結合句として内部結合すると、両方のテーブルに存在する顧客・注文情報だけが残ります。

したがって、注文履歴のある顧客の情報をまとめた表が作成されます。会員登録をせずに注文した情報や、会員登録だけして一度も注文していない顧客の情報は、内部結合をした後のテーブルには残りません。

”customer”テーブルと”orders”テーブルを内部結合した表

左結合(LEFT JOIN)

定義:2つのテーブルの共通項目について、左側のテーブルのデータはすべて残し、右側のテーブルのデータは結合句の文字列もしくは値が一致している(条件に合う)行のみを残す結合方法です。左側のテーブルのデータはすべて保持され、右側のテーブルのデータは下記のように扱われます。
①左側のテーブルと結合句が一致している(条件に合う)行は残ります
②左側のテーブルの結合句にはあるが、右側のテーブルには無い(条件に合わない)ものは「NULL」で埋められます
③左側のテーブルの結合句にない右側のテーブルのデータは残りません

具体例:
”customer”テーブルを左側、”orders”テーブルを右側に置き、「顧客ID」を結合句としてデータを左結合します。 ”customer”テーブルにあるすべてのデータと、それに対応する”orders”のデータを残し、対応しないものは「NULL」と表示されます。

したがって、注文履歴のある顧客には注文情報が、ない顧客にはNULLが表示された顧客情報一覧が作成されます。顧客情報を基準にしているので、顧客情報にない注文情報(例えば、会員登録をせずに注文した情報)は残りません。

”customer”テーブルを左に置いて”orders”テーブルと左結合した表

一方で、”orders”テーブルを左側、”customer”テーブルを右側に置いて左結合した場合、 ”customer””テーブルにあっても、”orders”にない(一度も注文履歴がない)顧客の情報は、結合後のテーブルには残りません。

”orders”テーブルを左に置いて”customer”テーブルと左結合した表

右結合(RIGHT JOIN)

右結合は、左右のどちらに基準となるテーブルを置くかが左結合とは逆なだけで、操作の中身自体は左結合と同じです。

非推奨と言うほどではないですが、左を基準にすることが多いので、右結合が使われることは少ないです。

完全外部結合(FULL OUTER JOIN)

定義:2つのテーブルの共通項目について、左側のテーブルのデータ・右側のテーブルのデータの両方をすべて残し、結合句の文字列もしくは値が一致している(条件に合う)行のみ横につなげる結合方法です。左側・右側のテーブルのデータはそれぞれすべて残り、下記のように扱われます。
①左側のテーブルと結合句が一致している(条件に合う)行は横につなげられます
②左側のテーブルの結合句にはあるが、右側のテーブルには無いもの(条件に合わないもの)は、右側のデータが「NULL」で埋められます
③右側のテーブルの結合句にはあるが、左側のテーブルには無いもの(条件に合わないもの)は、左側のデータが「NULL」で埋められます

具体例:
”customer”テーブルを左側、”orders”テーブルを右側に置き、共通項目「顧客ID」でデータを完全外部結合します。
両方のテーブルの全データを表示し、どちらかにしかない場合は「NULL」で補完されます。

したがって、注文履歴がある顧客には対応する注文情報が表示され、注文履歴がない顧客には注文情報の欄がNULLで補完された表が作成されます。また、会員登録をせずに注文をしたユーザーについても、顧客情報の欄がNULLで補完されます。

”customer”テーブルと”orders”テーブルを完全外部結合した表

以上のように、結合方法には4つの種類があります。

サンプルデータを使った結合の実践例に入る前に、結合を行う前の重要なポイントについて押さえておきましょう。そのうえで、3つの結合パターンでの実践例について読むことをおすすめします。


結合前に確認すべきデータのポイント

「結合を行う前には、必ずデータの中身を確認すること」をおすすめします。

どのようにデータが結合されるかを決めるのは「データの品質・粒度・結合句」の3点ですが、これを確かめる際、自分の目でサンプル行を直接確認することがかなり有効です。(※粒度:データ1行がどれくらい細かい単位を表しているか)。
これにより、後工程でのトラブルを未然に防ぐことができます。

結合前にデータの中身を確認する際は、以下の点に注意しましょう。

  • 結合句の重複・欠損の有無

  • データの粒度の違い

  • データ型とそのフォーマット

  • 値の分布と対象となる期間

  • 欠損・NULLの位置

もしこのような確認をしないまま結合すると、行数が意図せず増えたり、数値が水増しされたりするなど、想定外の挙動につながります。この後の実践例でも、そうした結合結果への影響について触れるので、データの中身を確認しながら進めましょう。


データ結合実践

このセクションでは私が勉強した時と同じ手順で、Tableau Publicを使った3つの結合パターンについて、図を用いて解説していきます。みなさんもご自身の手元で、データがどうなっているかを確認しながら進めることをおすすめします。

この後のセクションでも、冒頭でダウンロードリンクを貼ったECショップのサンプルデータを使います。使うテーブルはそれぞれのセクションで確認します。

結合①マスタデータ×マスタデータの場合

(※マスタデータ:システムの基準となるデータ)

”customer”テーブルと”loyalty”テーブルを使います。データの中身を1つずつExcelやTableauで確認したうえで、結合操作に進んでください。

今回は、会員の居住市別に年齢と保有ポイントの平均値を出すことを目的に進めていきます。

2つのテーブルを共通項目「顧客ID」を結合句にして内部結合します。結合結果は以下の画像のようになり、10行分の結果が得られます。

”customer”テーブルと”loyalty”テーブルを「顧客ID」で内部結合した結果の画面

シート作成の画面に移って、表を作っていきます。居住市別に年齢と保有ポイントの平均を出すために、以下の操作をしましょう。

  1. 「居住市」を行に、「メジャーネーム」を列に配置

  2. 「メジャーバリュー」をマークカードのテキストに配置

  3. 「年齢」「保有ポイント」は平均で集計

  4. アナリティクスタブから、列の総計を追加して表示

完成した表と併せて、事前に計算しておいた平均年齢・平均保有ポイントを以下の画像に示しています。これは今回の練習にあたって、Tableauで集計した値が想定どおりかを検算するためのものです。

”customer”テーブルと”loyalty”テーブルを「顧客ID」で内部結合して作ったテキスト表

表の値と事前に計算しておいた値を比べると、東京・大阪・全体の平均年齢・平均保有ポイントは、事前に計算していた値と一致する正しい集計値であることがわかります。今回は確認のために、先に集計値を計算しておきました。

さらに詳細を確認するために、以下の操作をしましょう。

  1. 「顧客ID」を行に追加

  2. 値を全選択してから右クリック

  3. 「データの表示」から「完全データ」を選択

”customer”テーブルと”loyalty”テーブルを「顧客ID」で内部結合した時の完全データ

今開いた完全データでは、結合後の1行1行について「居住市」「顧客ID」「年齢」「保有ポイント」の値を一覧で見ることができます。

この表で確認すべきポイントは次の2つです。

  • 「顧客ID」がすべてユニーク(一意)であり、1人の顧客に対して1行ずつのデータとなっていること

  • 集計対象である「年齢」「保有ポイント」の値が、全て、重複なく入っていること

以上2点を確認することで、結合の結果が想定どおりの集計となっていると判断できます。

”customer”テーブルの「居住市」「顧客ID」「年齢」が、”loyalty”テーブルの「保有ポイント」と紐づいています。このことから、2つのマスタデータが、「顧客ID」を基準につながっていると判断できます。

この結果は、結合句「顧客ID」に対して1行ずつすべてが1対1で対応しています。

「1行ずつすべてが1対1で対応」とは、どういうことかについて次に解説していきます。

図解

2つのデータの中の結合句「C001」の行を見てみましょう。

”customer”テーブルには1行、”loyalty”テーブルにも1行のデータがあります。この時、”customer”テーブルの「C001」の1行が、”loyalty”テーブルの「C001」の1行と対応し、1つのテーブルになります。

顧客ID「C001」だけで見て”customer”テーブルと”loyalty”テーブルを結合することの図

このような動きが各「顧客ID」で行われているので、”customer”テーブルと”loyalty”テーブルが「1行ずつ1対1で対応している」と言います。

そして、年齢や保有ポイントは想定していた正しい値となります。

※2つのデータが1対1で対応するのは、結合句がそれぞれの表の中でユニーク(一意)であるような列同士が対応する場合に限ります。

【マスタデータ×マスタデータで結合した場合のまとめ】

マスタデータ×マスタデータで結合した場合は、1行がマスタデータの1行単位になります。

列が増えるだけで、データが1対1で対応しているため、集計値も想定どおりの値となります。

私のような結合操作の初心者にとっては、イメージがしやすいパターンだと言えます。

結合②ログデータ×マスタデータの場合

(※ログデータ:1件のイベントを1行として時系列で記録されるデータ)

マスタデータではないものと結合すると、どうなるのでしょうか。

”order_items”テーブルと”customer”テーブルを使います。データの中身を1つずつExcelやTableauで確認したうえで、結合操作に進んでください。

今回は、会員の居住市別に注文金額の合計と平均、平均年齢を出すことを目的として進めていきます。

2つのテーブルを共通項目「顧客ID」を結合句にして内部結合します。結合結果は以下の画像のようになり、9行分の結果が得られます。

”order_items”テーブルと”customer”テーブルを「顧客ID」で内部結合した結果の画面

シート作成の画面に移って、表を作っていきます。居住市別で注文金額の合計と平均、平均年齢を出すために、以下の操作をしましょう。

  1. 「居住市」を行に、「メジャーネーム」を列に配置

  2. 「メジャーバリュー」をマークカードのテキストに配置

  3. 注文金額は合計と平均、年齢は平均をセット

  4. アナリティクスタブから、列の総計を追加して表示

完成した表と併せて、事前に計算しておいた検算用の各値を画像に示しています。

”order_items”テーブルと”customer”テーブルを「顧客ID」で内部結合して作ったテキスト表

見比べると、東京・大阪・全体での平均年齢が、想定していた値とは異なる結果になりました。

粒度を細かくしてデータの中身をチェックするために、以下の操作をしましょう。

  1. 「顧客ID」「オーダーID」「商品ID」を行に追加

  2. 値を全選択してから右クリック

  3. 「データの表示」から「完全データ」を選択

”order_items”テーブルと”customer”テーブルを「顧客ID」で内部結合した時の完全データ

1行ずつ確認すると、1顧客が複数の商品を注文している場合に、各注文のすべての行に顧客の年齢が当てはめられています。「年齢の欄に同じ値が並んでいる」ことから「マスタデータの行が複製されている」ことが分かります。

よって、集計結果が想定している値と異なる値になったのです。

このような状態を「1対多」の関係と言います。

行が複製されている状態は、現実には発生していないデータを作り出していることを意味します。

今回の場合、「注文の回数分、同じ年齢・同じ顧客IDのクローンが作られた」ような状態になっています。そのクローンも合わせた人数分の平均年齢を取るような計算をするため、求めたい平均年齢とは異なるものになってしまいました。

なぜこのようなことが起こるのでしょうか?

ログデータと粒度の異なるマスタデータを1対多(n:1)で結合すると、マスタデータの行がログデータの各行に複製されます。すると、マスタデータは複製分まで合計されるため、最終的な合計値が実際より大きくなるのです。

言葉だけで書かれても私は理解が難しいと感じたため、さらに図解していきます。

図解

2つのデータの中で、結合句「C001」のものだけを見てみましょう。ログデータの”order_items”テーブルには3行、マスタデータの”customer”テーブルには1行のデータがあります。

顧客ID「C001」だけで見て”order_items”テーブルと”customer”テーブルを結合することの図

マスタデータの1行がログデータの3行にそれぞれつながります。

1対多の関係と言われるのは、マスタデータの「1」行に対して、「複数」行のログデータが紐づいているためです。マスタデータの行が複製され、集計値にズレが生じたのです。

結果として、現実には発生していないデータ(同じID、同じ年齢のクローン人間のようなもの)が作り出されてしまうのです。

【ログデータxマスタデータの場合のまとめ】

ログデータ×マスタデータの場合は、1行がログデータの1イベント単位になります。

結合後はマスタデータの行に重複が起こり、「現実には発生していないデータ」を作り出してしまいます。

この状態で集計を行うと、集計値が想定と異なる結果になる可能性があるため、利用時には注意が必要です。

リレーションシップを活用すると、この問題は回避できます。詳しくは次回の記事で紹介します。

結合③ログデータ×ログデータ

最後に、ログデータ同士の結合ではどうなるでしょうか。

”order_items”テーブルと”promos”テーブルを使います。データの中身を1つずつExcelやTableauで確認したうえで、結合操作に進んでください。

今回は、商品別に注文金額の合計・平均と割引後の注文金額の合計を出すことを目的に進めていきます。

2つのテーブルを共通項目「商品ID」を結合句にして左結合します。結合結果は以下の画像のようになり、18行分の結果が得られます。

”order_items”テーブルと”promos”テーブルを「商品ID」で左結合した結果の画面

シート作成の画面に移って、表を作っていきます。商品別に注文金額の合計・平均と割引後の注文金額の合計を出すために、以下の操作をしましょう。

まずは、割引後の注文金額を求めるための計算フィールドを作ります。「計算フィールドの作成」から、下記のような式を入力してください。

[注文金額]*(1-IFNULL([割引率],0))

このような式にすることによって、結合で対応したキャンペーン期間による割引があるもの・ないものを区別して計算することができます。

次に、以下の操作をして表を作りましょう。

  1. 「商品ID」を行に、「メジャーネーム」を列に配置

  2. 「メジャーバリュー」をマークカードのテキストに配置

  3. 求めたい注文金額の合計・平均と割引後の注文金額の合計の3つをセット

  4. アナリティクスタブから、列の総計を追加して表示

完成した表と併せて、事前計算しておいた検算用の各値を画像に示しています。

”order_items”テーブルと、”promos”テーブルを「商品ID」で内部結合して作ったテキスト表

見比べると、どのメジャーの集計値も想定していた値とは異なる結果になりました。

粒度を細かくしてデータの中身をチェックするために、以下の操作をしましょう。

  1. 「キャンペーンID」「オーダーID」「顧客ID」を行に追加

  2. 「注文日」を「正確な日付」で列に追加

  3. 値を全選択してから右クリック

  4. 「データの表示」から「完全データ」を選択

”order_items”テーブルと”promos”テーブルを「商品ID」で内部結合した時の完全データ

1行ずつ確認すると、注文金額・割引後注文金額・割引率・注文日で同じ値が何度も出てきています。さらに、結合句「商品ID」ごとに見ると、「キャンペーンID」「オーダーID」ですべて異なる組み合わせが作られています。

よって、集計結果が想定している値と異なる値になったのです。先ほど作った式も、データの組み合わせが複数あることで、想定していた通りの集計ができなくなっています。

このような状態を、「多対多」の関係と言います。

今回の場合、同じ顧客が同じ日に同じ商品を別々のキャンペーンで購入した事実はないはずなのに、そういったデータができてしまいました。

では、なぜこのようなことが起こるのでしょうか?

「商品ID」という結合句を基準に、ログデータ1行に対してもう1つのログデータで該当するすべての行がつなげられたことで、現実には発生していないデータができてしまったのです。

ログデータ同士を多対多(n:n)で結合すると、同じ結合句のデータがどちらのログデータにも複製されます。すると、行が掛け算のように増えてしまい、集計値が実際とはズレた結果になるのです。

言葉だけでは私は理解が難しいと感じたため、さらに図解していきます。

図解

2つのデータの中で、結合句である「P002」のものだけを見ます。ログデータの”order_items”テーブルには3行、もう1つのログデータの”promos”テーブルには2行のデータがあります。

商品ID「P002」だけで見て”order_items”テーブルと”promos”テーブルを結合することの図

”order_items”テーブルの3行と”promos”テーブルの2行のそれぞれの組み合わせを作るようにつなげられます。今回注目した結合句「P002」のデータは、3 × 2 = 6 行に行数が膨れ上がります。

多対多の関係と言われるのは、結合句を基準に「複数行」対「複数行」でつながるためです。

このように、複製された行数の増加に伴って集計値にズレが生じ、現実で起きた事とは異なるデータができあがりました。

この問題を回避するためには、”order_items”テーブルの「注文日」と”promos”テーブルの「開始日」「終了日」をそれぞれ対応するように結合句に追加する必要があります。こうすることによって、実際に起きている状況のままのデータにすることができます。下の画像を参考にお手元で試し、先ほどと同じシートを作ってみてください。

多対多により事実とデータが変わることを回避するための結合例

【ログデータxログデータの場合のまとめ】

ログデータ×ログデータの場合は、1行がログデータの1イベントの組み合わせ数の単位になります。

多対多での結合後は行に重複が起こり、「現実には発生していないデータ」を作り出してしまいます。

この状態で集計を行うと、集計値が想定と異なる結果になる可能性があるため、利用時には注意が必要です。

データが1対1で対応するように結合句を設定することで、この問題を回避することができます。

補足

現実には、マスタデータやログデータが想定しているような構造ではなく、きれいに整っていないこともあります。

例えば、「1行が顧客と対応しているマスタデータ」と認識していたものの、実際には「顧客の契約状況を表す履歴データ」だったという想定外のケースも存在します。

このような場合、過去の契約履歴を保持していたために顧客IDが重複しており、意図しない集計結果につながることがあります。

元のデータの1行がどのような単位なのかを意識し、そのうえで結合や集計処理をすることは非常に重要です。

特に結合を行う場合は、粒度の違いによって発生する誤集計や意図しない複製、現実には発生していないデータの抽出に注意する必要があります。


まとめ

この記事を通して「結合とはどんな操作なのか」についての理解が深まっていれば嬉しいです。

結合の実践として紹介した3つの結合について、簡単にまとめます。

①マスタデータ×マスタデータ(同じ概念の一意な列同士※)の結合の場合
結合句を基準に1対1でデータをつなげることができ、想定どおりの集計ができた。(※「顧客ID=顧客1人につき1行」といったように、結合句がどちらの表の中でも重複していない場合に限る。同じものを表す列同士が対応していれば、1対1で結合できる。結合句に重複があると、1対多や多対多になる。)

②ログデータ×マスタデータの結合の場合
結合句を基準に1対多でデータがつながり、マスタ側の行が複製され、現実には発生していないデータを作り出してしまったことで、実際に集計して得たい値とは異なる結果になった。
結合後のテーブル1行の粒度を確認し、想定通りの集計ができるデータになっているかをチェックすることが必要。リレーションシップを活用すると、先述の問題を回避できる(次回の記事で紹介)。

③ログデータ×ログデータの結合の場合
結合句を基準に多対多でデータがつながり、掛け算のように行が複製され、現実には発生していないデータを作り出してしまったことで、実際に集計して得たい値とは異なる結果になった。
今回の場合、データが1対1で対応するように結合句を設定することで、先述の問題を回避した。結合後のテーブル1行の粒度を確認し、想定通りの集計ができるデータになっているかをチェックしたうえで、利用するデータに合わせた対応が必要。

分析の際は、データの中身を確認してから、適切に結合する必要があります。そのうえで、実際に自分の手でさまざまなデータ結合をすることで、理解がさらに深まります。

皆さんの「結合」機能に対する理解の助けになれば幸いです。

【次回予告】

別の記事で、今回の実践例で起きたような「結合によって行の重複が発生し、集計結果が思ったとおりにならない」というケースで使える「リレーションシップ(関係)」という機能について紹介しています。

データ同士をつなげるときの考え方の基本として結合から紹介しましたが、Tableauの公式ドキュメントでは、ほとんどのケースで結合よりもリレーションシップ機能の利用が推奨されています。

リレーションシップについても、皆さんが手元で操作・確認をしながら読める内容にする予定なので、次回の記事も読んでいただけたら嬉しいです!

参考

私が結合について勉強した時に、Satoshi Ganekoさんのnoteを参考にさせていただきました。サイトのリンクを貼らせていただきます。

Tableau の 結合、リレーションシップ(リレーション)、データブレンド(ブレンディング)を理解する

この記事をシェアする

minoRi

データをわかりやすく伝えるお手伝いをしています📈
Tableau Certified Data Analyst(2025年4月取得)

ホームツール

Tableauにおけるデータ結合とは