ループ構造の回避

ループ構造の回避

Pocket

リロードを実行した際「データベース構造にループが存在します。」という警告が表示されることがあります。
この警告が表示された場合、データの関連付けが一部強制的に解除されるため、警告が表示されないように修正する必要があります。
ここでは、なぜこの警告が表示されるかと、この問題の解決方法について説明します。

この記事の対象は、データロードの基礎(データの取り込み、項目名の変更)を身につけた方を想定しています。基礎に不安のある方は、事前に以下の記事で学んでからこの記事をご覧ください。
ウィザードを使わないデータの取り込み
データの関連付け


リロードを実行した際、以下の警告が表示されることがあります。
cut-the-loop-101警告の全文は以下のとおりです。

データベース構造にループが存在します。ループはあいまいな結果を招く可能性があり、避ける必要がありますので QlikView が疎結合機能をテーブルに設定しループを切ります。疎結合機能の設定はロード スクリプト終了後、ドキュメント プロパティのテーブル画面で修正することができます。

この警告はループ構造がある場合に表示されます。
ここで言うループ構造とは、テーブルの関連付けが下図のように循環している状態を指します。
cut-the-loop-102
QlikViewでは値を選択すると、それに紐付く値が絞り込まれていきます。ループ構造があると、値の絞り込みがうまくできなくなるため、ループ構造はなくさなければなりません。

ここでは、ループ構造の解決方法について、三つの例をとおして説明します。
また後半部分では、中級者~上級者向けに、なぜループ構造が発生するのかデータモデルの視点から説明します。

ファイルのダウンロード
ループ構造の回避1(単純な間違いによるループ)
ループ構造の回避2(もとのテーブル構造に起因するループ)
ループ構造の回避3(もとのテーブル構造に起因するループ)
ループ構造とデータモデル
振り返り
最後に…


ファイルのダウンロード

ここでは以下のファイルを使用しますので、ダウンロードしてください。
cut-the-loop.zip
Zip形式ですので、ダウンロード後解凍してください。
以下ファイルが含まれています。
cut-the-loop-103
売上実績.xls
今回使用するデータです。
cut-the-loop-104
ロードスクリプト1.txt~ロードスクリプト3.txt
今回使用するロードスクリプトです。今回のチュートリアルは、ロードスクリプトを新規に作成する部分は本題ではないため、ロードスクリプトはテキストファイルから貼り付けて作成します。
cut-the-loop-105「ロードスクリプト完成版」フォルダには完成版のロードスクリプトが入っています。


ループ構造の回避1(単純な間違いによるループ)

それではデータを取り込んでみましょう。
まずはファイルを新規に作成し、[ロードスクリプトの編集]画面を起動します。
最下行にカーソルを合わせてください。
cut-the-loop-106
今回はロードスクリプトはあらかじめ用意してあります。
「ロードスクリプト1.txt」を開き、すべてコピーします。
cut-the-loop-107cut-the-loop-108
ロードスクリプトを貼り付けて、[OK]ボタンをクリックしてください。
cut-the-loop-109Excelファイルへのパスは相対パスで指定してあります。(ファイル名のみ指定してあります。)
そのため、このあとQVWファイルを保存する際、Excelファイルと同じ場所に、QVWファイルを保存してください。

リロードを実行してみましょう。
[リロード]ボタンをクリックし、ファイルを保存してください。
cut-the-loop-110cut-the-loop-111
Excelファイルと同じ場所に保存してください。
cut-the-loop-112
警告が表示されました。前述のとおり、この警告はループ構造がある場合に表示されます。
cut-the-loop-113
[ロードスクリプトの進捗]画面は通常どおりです。
ループ構造があっても、データの関連付けが正しくおこなわれないだけで、データ自体はすべて取り込まれます。
cut-the-loop-114
それでは、テーブル構造を確認してみましょう。
[テーブルビューアー]ボタンをクリックします。
cut-the-loop-115
テーブル構造が表示されました。
cut-the-loop-116このままでは分かりにくいため、下図のように配置しなおします。

ここで、ループ構造があった場合の動作について確認しておきましょう。
まずループ構造があった場合、QlikViewはループ構造を回避するために一部の関連付けを強制的に解除します。
下図で点線になっているところが、QlikViewによって関連付けが解除された部分で、この部分は関連付けがされていないのと同等です。
cut-the-loop-117どこの関連付けを解除するかはQlikViewが自動で判断しますが、何度かためした限りでは、データの件数がもっとも多いテーブル(件数がもっとも多いテーブルが複数ある場合は、ロードスクリプトの上に記述されたテーブル)で、関連付けが解除されるようです。
なお、点線で結ばれた結合のことを疎結合、疎結合になっているテーブルのことを、疎結合テーブル(Loosen Table)と呼びます。
ここでは、売上明細テーブルが疎結合テーブルです。

また、画面左側の納品テーブルにも注目してください。納品テーブルはループ構造とは直接関係ありません。しかし、売上明細テーブルが疎結合テーブルなので、売上明細テーブルと納品テーブルの関連付けも解除されています。(納品→売上明細の線が点線になっています。)
このように、一部でループ構造が発生すると、ループ構造と直接関係ないテーブルであっても、関連付けが解除されることがあります。

それでは、ループ構造を回避しましょう。
ループ構造が発生している原因自体は非常に単純です。
担当者の名前と部門の名前が、両方「名前」という項目名になっています。項目名が重複しているために、余分な関連付けが発生しており、その結果ループ構造が発生しています。
cut-the-loop-118
[ロードスクリプトの編集]画面を開き、項目名を変更します。
部門テーブルの「名前」は「部門名」に、担当者テーブルの「名前」は「担当者名」に変更します。
cut-the-loop-119ループ構造を回避するだけであれば、どちらか一方の項目名を変更するだけで構いませんが、ここでは分かりやすいように両テーブルの項目名を変更しています。

リロードを実行しましょう。
cut-the-loop-120
こんどは警告は表示されません。
cut-the-loop-121
テーブルビューアーを確認すると、ループ構造がなくなっていることが分かります。
cut-the-loop-122
以上で一つ目のチュートリアルは終了です。つぎのチュートリアルでは新規にドキュメントを作成しますので、ここで作成したドキュメントは閉じておいてください。


ループ構造の回避2(もとのテーブル構造に起因するループ)

一つ目の例は、単純に項目名が重複しているという問題でした。
しかし、データによっては、構造上そのままではどうしてもループが発生してしまう場合があります。
さっそく試してみましょう。

まずはファイルを新規に作成し、[ロードスクリプトの編集]画面を起動します。
最下行にカーソルを合わせてください。
cut-the-loop-201
前回同様、ロードスクリプトをコピーしてください。
今回使用するのは「ロードスクリプト2.txt」です。
cut-the-loop-202cut-the-loop-203
ロードスクリプトを貼り付けて、[OK]ボタンをクリックしてください。
cut-the-loop-204
リロードを実行してみましょう。
[リロード]ボタンをクリックし、ファイルを保存してください。
cut-the-loop-205cut-the-loop-206
Excelファイルと同じ場所に保存してください。
cut-the-loop-207
ループ構造が発生しました。
cut-the-loop-208cut-the-loop-209
テーブルビューアーを開いて、テーブル構造を確認してみましょう。
cut-the-loop-210
テーブル構造が表示されました。
cut-the-loop-211
このままでは分かりにくいため、下図のように配置しなおしてください。
cut-the-loop-212今回ループ構造が発生しているのは、受注テーブルと売上明細テーブルが、両方とも部門テーブルと紐付いているためです。

これは、前回のように項目名が間違っているわけではなく、業務で使用するデータベースにはよくあることです。しかし回避方法は非常に単純です。
  • 受注テーブルの部門と、売上明細テーブルの部門が同じ部門を指す場合
    重複してデータを持つ必要はないので、片方から削除してください。たとえば、受注テーブルのLOAD文から「部門番号」の項目を削除します。
  • 受注テーブルの部門と、売上明細テーブルの部門が違う部門を指す場合
    部門テーブルをもう一つロードし、別の名前を付けてください。
ここでは、受注した部門と売上を上げた部門が、別の部門であるという前提で、部門テーブルをもう一つロードしてみます。

[ロードスクリプトの編集]画面を開きます。
部門テーブルのLOAD文をコピーしてください。
cut-the-loop-213
貼り付けて複製してください。
cut-the-loop-214
いま貼り付けた方を以下のように編集し、テーブル名と項目名を変更します。
受注部門:
LOAD 部門番号 as 受注部門番号, 
     部門名 as 受注部門名
cut-the-loop-215
受注テーブルの「部門番号」を以下のように変更します。
cut-the-loop-216
リロードを実行してください。こんどは警告は表示されません。
cut-the-loop-217
テーブルビューアーでテーブル構造を確認してみましょう。
部門テーブルを二つに分けたため、ループ構造がなくなりました。
cut-the-loop-218ここまででループ構造は回避できましたが、部門テーブルを二回読み込んでいるのが非効率に感じます。
ロードスクリプトには、読み込み済みのテーブルからデータを再読み込みする、「Resident」という機能が用意されています。

[ロードスクリプトの編集]画面を開いてください。
受注部門の方の「FROM」以降を削除します。
cut-the-loop-219
以下の指定に差し替えます。
Resident 部門;
cut-the-loop-221途中まで入力すれば、自動で候補がでてきます。cut-the-loop-220
リロードを実行してください。
結果はなにも変わりませんが、Excelファイルから二重に読み込んでいた部分がなくなりました。
部門テーブルは数件しかないため、リロード時間はほとんど変わりませんが、データの件数が多い場合、Residentをうまく使うと、リロード時間を短縮できます。
cut-the-loop-222
以上で二つ目のチュートリアルは終了です。つぎのチュートリアルでは新規にドキュメントを作成しますので、ここで作成したドキュメントは閉じておいてください。


ループ構造の回避3(もとのテーブル構造に起因するループ)

さいごにもう一つ別の例を試してみましょう。
まずはファイルを新規に作成し、[ロードスクリプトの編集]画面を起動します。
最下行にカーソルを合わせてください。
cut-the-loop-301
前回同様、ロードスクリプトをコピーしてください。
今回使用するのは「ロードスクリプト3.txt」です。
cut-the-loop-302cut-the-loop-303
ロードスクリプトを貼り付けて、[OK]ボタンをクリックしてください。
cut-the-loop-304
リロードを実行してみましょう。
[リロード]ボタンをクリックし、ファイルを保存してください。
cut-the-loop-305cut-the-loop-306
Excelファイルと同じ場所に保存してください。
cut-the-loop-307
今回はループ構造は発生しませんでしたが、Syntheticテーブルができています。
cut-the-loop-308
テーブルビューアーを開いて、テーブル構造を確認してみましょう。
cut-the-loop-309
やはりSyntheticテーブルができています。
これは、売上明細テーブルと担当者テーブル、両方に「部門番号」があるためです。
今回はループ構造ではなく、Syntheticテーブルでしたが、一つのテーブルを複数の箇所から参照していると言う意味では、一つ前の例と同じです。
cut-the-loop-310※Syntheticテーブルを確認するには、画面右上で[内部テーブルビュー]を選択します。

Syntheticテーブルを回避してみましょう。
[ロードスクリプトの編集]画面を起動します。
ロードスクリプトの最下部に、以下の指定を追加します。
所属部門:
LOAD 部門番号 as 所属部門番号, 
     部門名 as 所属部門名
Resident 部門;
cut-the-loop-311
担当者テーブルの「部門番号」を以下のように変更します。
cut-the-loop-312
リロードを実行します。
こんどはSyntheticテーブルは作成されません。
cut-the-loop-313
テーブルビューアーでテーブル構造を確認してみましょう。
Syntheticテーブルがなくなりました。
cut-the-loop-314


ループ構造とデータモデル

以降の説明は、QlikViewとは直接関係ないデータモデルに関する説明です。QlikViewを使う上で、ここに書かれた知識は必須のものではありません。QlikViewについてさらに理解を深めたい中級者~上級者以外の方は、読み飛ばしていただいて構いません。

ここまでで三つの例をご覧いただきました。
一つ目の例は単純に項目名が適切でないという問題でしたが、二つ目と三つ目の例はもとのテーブル構造に起因する問題でした。
QlikViewの話から外れますが、さいごにどうしてこのようなデータの持ち方をするのか、まとめてみます。

二つ目の例

二つ目の例は以下のテーブル構造でした。
cut-the-loop-212
売上明細テーブルと受注テーブルの両方に、「部門番号」を持たせています。
これは結合先によって意味が変わるデータを、1つのテーブルにまとめているということです。

受注した部門と、売上を上げた部門は同じ部門であることが多いかもしれません。そのため今回の例は、あまりよい例ではなかったかもしれません。
しかし、このように一つのテーブルが結合先のテーブルによって別の意味で使われるのは、実際のデータベースではあり得ることです。
たとえば、受注担当者、出庫担当者、売上担当者(営業マン)が、すべて社員テーブルを参照していると言うのは、業務で使用するデータベースではよくあることです。


三つ目の例

三つ目の例は以下のテーブル構造でした。
cut-the-loop-310
売上明細テーブルと担当者テーブルの両方に、「部門番号」を持たせています。
「部門番号」が重複しているという点では、二つ目の例と同じです。しかし、ある担当者はかならず一つの部門に所属しているため、売上明細テーブルには担当者番号さえあれば、売上明細→担当者→部門と紐付けられそうです。
そう考えると、二つ目の例とはまた別の課題と考えられます。

ではなぜ上のようなデータの持ち方をしているかと言うと、マスターの変更に対応するために、データを重複して持っているからです。

ここで言うマスターとは、商品や社員など、あとから変更されないデータのことを指します。しかし、業務においてはマスターであっても、あとから変更される可能性があります。たとえば、商品の名称変更、価格改定、組織変更、移転などによって、マスターと言えど、変更が入ることを考慮しなければなりません。
このように、(まれに)変更されるマスターのことを、SCD(Slowly Changing Dimension)と呼び、データウェアハウスやBIの世界では有名な課題です。

今回の例で言えば、組織変更に対応するために、販売時の部門を売上明細テーブルに持たせているということになります。
たとえば、8月に東京支店の田中さんが1,000円の売上を上げたとします。そのあと9月に田中さんが大阪支店に異動になったとします。
この場合、8月の東京支店の売上から、1,000円を大阪支店に付け替えるでしょうか?付け替えはしないと思います。
つまり、8月までの田中さんの成績は東京支店の成績、9月以降の田中さんの成績は大阪支店の成績とするのが一般的だと思います。

組織変更に対応するためには、異動前の部門の情報をどこかに持たせておかなければなりません。今回の例では売上明細テーブルに販売時の部門番号を持たせています。
逆に、担当者テーブルの部門番号は、その社員が現時点で所属している部門をあらわしています。


少し難しく感じるかもしれませんが、いずれの課題も、QlikViewでは同じ意味を持つデータは同じ名前(テーブル名、項目名)にする、違う意味を持つデータは違う名前(テーブル名、項目名)にするという一点にまとめられます。
この原理・原則を意識していただくことで、データロード時にトラブルが発生しても、解決策が想像できるのではないかと思います。


振り返り

ここではループ構造の回避についてご覧いただきました。以下に重要なポイントをまとめます。
  • ループ構造がある場合、データの関連付けが一部強制的に解除されるため、回避しなければならない。
  • 関連付けが解除された部分はテーブルビューアー上、点線で表示される。
  • 関連付けが解除された結合を疎結合、疎結合になっているテーブルを、疎結合テーブル(Loosen Table)と呼ぶ。
  • ループ構造の回避方法はデータによるので一概には言えないが、同じ意味を持つデータが同じ名前になるように、テーブル名や項目名を変更したり、一つのテーブルを複数回読み込んだりすることで回避できる。
  • 「Resident」を使用すると、ロードスクリプトで読み込み済みのテーブルからデータを再読み込みできる。
    Resident 読み込み済みのテーブル名


最後に…

前回、データの関連付けについてご覧いただきましたが、データロードにおいては、Syntheticテーブルとループ構造が発生しないようにするのが重要です。
今回のチュートリアルまでご理解いただければ、データロードの基礎は十分に身に付いたと言えます。

このつぎのステップは、ロードスクリプトのさまざまな関数を使ってデータを加工するといった、一段上のステップになるかと思います。

お疲れ様でした。