9. 結合モデルと集計モデルの定義

このチュートリアルでは DMDL を利用してAsakusa Frameworkで利用する データモデル を定義する方法を説明していきます。

DMDLによるデータモデルの定義 の続きで、ここでは 結合モデル集計モデル を作成していきます。

9.1. 結合モデルを作成する

結合モデルは、ある2つのデータモデルに対して「結合」の操作を行って生成するデータモデルを表します。

データモデルの結合とは、結合対象となる2つのデータモデルが持つそれぞれのプロパティを組み合わせたデータモデルを生成する処理のことです。 データモデルの結合には、データモデルを付き合わせるためにそれぞれのデータモデルがもつプロパティを 結合キー として指定します。

結合モデルを作成しておくことで、データフロー内で結合に関する処理を実装することなく、結合結果のデータモデルを取得することができるようになります。

このチュートリアルでは、サンプルアプリケーションの説明 で紹介した 売上明細 ( sales_detail )商品マスタ ( item_info ) を結合した結合モデルを作成していきます。

下図は、これから作成する結合モデルがデータフローのどこで使用されているかを示しています。

_images/example-app-dataflow-dmdl-2-1.png

9.1.1. 結合モデルの定義

DMDLスクリプト models.dmdljoined_sales_info というデータモデル名で結合モデルを追加します。 models.dmdl 内に追加する場所はどこでもかまいません。 ここではデータモデル item_info の直後に追加します。

models.dmdl
"売上明細+商品マスタ"
joined joined_sales_info
= sales_detail -> {

    "商品コード"
    item_code -> item_code;

    "販売数量"
    amount -> amount;

    "売上合計"
    selling_price -> selling_price;
} % item_code
+ item_info -> {

    "商品コード"
    item_code -> item_code;

    "カテゴリコード"
    category_code -> category_code;
} % item_code;

結合モデルにはいくつかの記述方法がありますが、ここでは以下のような構造で作成しています。

joined <結合モデル名>
= <対象データモデル1> -> {
    <結合前プロパティ名> -> <結合後プロパティ名>;
    <結合前プロパティ名> -> <結合後プロパティ名>;
    ...
} % <結合キー1>
+ <対象データモデル2> -> {
    <結合前プロパティ名> -> <結合後プロパティ名>;
    <結合前プロパティ名> -> <結合後プロパティ名>;
    ...
} % <結合キー2>;

上から順番に見ていきます。結合モデルはデータモデル名の前に joined キーワードを指定します。

models.dmdl
"売上明細+商品マスタ"
joined joined_sales_info
= sales_detail -> {

    "商品コード"
    item_code -> item_code;

    "販売数量"
    amount -> amount;

    "売上合計"
    selling_price -> selling_price;
} % item_code
+ item_info -> {

    "商品コード"
    item_code -> item_code;

    "カテゴリコード"
    category_code -> category_code;
} % item_code;

データモデル名の定義に続けて、結合の対象となるデータモデルに関する情報を定義します。 まず、売上明細部分の定義を見ていきます。

models.dmdl
"売上明細+商品マスタ"
joined joined_sales_info
= sales_detail -> {

    "商品コード"
    item_code -> item_code;

    "販売数量"
    amount -> amount;

    "売上合計"
    selling_price -> selling_price;
} % item_code
+ item_info -> {

    "商品コード"
    item_code -> item_code;

    "カテゴリコード"
    category_code -> category_code;
} % item_code;

対象データモデル名に続けて、{...} で囲われたブロック内でプロパティのマッピング情報を記述します。

item_code -> item_code; となっている行を例に説明すると、

  • 結合前のデータモデル sales_detail が持つプロパティ item_code
  • 結合後のデータモデル joined_sales_info のプロパティとして、 item_code という名前で利用する

という意味になります。この例では、結合前のプロパティと結合後のプロパティが同じ名前ですが、 この仕組みによって結合前と後で異なるプロパティにマッピングすることができるようになっています。

なお sales_detail が持つプロパティのうち、ここに書かれていないプロパティ(例えば sales_date_time など)は結合モデルには引き継がれず、捨てられることになります。

最下行の % item_code では結合キーとなるプロパティを指定しています。 マッピング前後でプロパティ名を変更する場合、このプロパティ名は 結合後プロパティ名 を指定する必要があることに注意してください。

続けて商品マスタ側の定義を見てみます。

models.dmdl
"売上明細+商品マスタ"
joined joined_sales_info
= sales_detail -> {

    "商品コード"
    item_code -> item_code;

    "販売数量"
    amount -> amount;

    "売上合計"
    selling_price -> selling_price;
} % item_code
+ item_info -> {

    "商品コード"
    item_code -> item_code;

    "カテゴリコード"
    category_code -> category_code;
} % item_code;

キーワード + に続けて sales_detail と同様に結合対象となるデータモデル名、プロパティのマッピング情報、結合キーを指定しています。

このデータモデルの定義の結果、結合モデル joined_sales_info は以下のような構造をもつデータモデルとして定義されます。

結合モデル ( joined_sales_info )
プロパティ名 説明 結合元データモデル
item_code 商品コード (結合キー) sales_detail = item_info
amount 数量 sales_detail
selling_price 販売金額 sales_detail
category_code カテゴリコード item_info

結合モデルの定義では、結合条件は同じ値をもつキー値で結合する 等価結合条件 のみを指定することができます。 これ以外の条件で結合する 非等価結合条件 を指定する場合は、異なる方法で結合条件を記述することになります。

非等価結合の実装方法は、この後のチュートリアル 演算子の作成 で説明します。

9.2. 集計モデルを作成する

集計モデルは、ある1つのデータモデルに対して「集計」の操作を行った結果として生成するデータモデルを表します。

集計モデルを作成しておくことで、データフロー内で集計に関する処理を実装することなく、集計結果のデータモデルを取得することができるようになります。

このチュートリアルでは、結合モデルを作成する で作成した 結合モデル ( joined_sales_info ) に対して集計を行い、 カテゴリ別売上集計 ( category_summary ) を生成する集計モデルを作成していきます。

下図は、これから作成する結合モデルがデータフローのどこで使用されているかを示しています。

_images/example-app-dataflow-dmdl-2-2.png

9.2.1. 集計モデルの定義

前のチュートリアル DMDLによるデータモデルの定義 では カテゴリ別売上集計 ( category_summary ) はレコードモデルとして定義しました。

models.dmdl
"カテゴリ別売上集計"
category_summary = {

    "カテゴリコード"
    category_code : TEXT;

    "販売数量"
    amount_total : LONG;

    "売上合計"
    selling_price_total : LONG;
};

このデータモデルを、以下のように集計モデルによるデータモデル定義に変更します。

models.dmdl
"カテゴリ別売上集計"
summarized category_summary = joined_sales_info => {

    "カテゴリコード"
    any category_code -> category_code;

    "販売数量"
    sum amount -> amount_total;

    "売上合計"
    sum selling_price -> selling_price_total;
} % category_code;

集計モデルは以下のような構造で定義します。

summarized <集計モデル名> = <対象データモデル> => {
    <集約関数> <集計対象のプロパティ名> -> <集計結果のプロパティ名>;
    <集約関数> <集計対象のプロパティ名> -> <集計結果のプロパティ名>;
    ...
} % <グループ化キー>;

上から順番に見ていきます。集計モデルはデータモデル名の前に summarized キーワードを指定します。 データモデル名の定義に続けて、集計の対象となるデータモデルを指定します。

models.dmdl
"カテゴリ別売上集計"
summarized category_summary = joined_sales_info => {

    "カテゴリコード"
    any category_code -> category_code;

    "販売数量"
    sum amount -> amount_total;

    "売上合計"
    sum selling_price -> selling_price_total;
} % category_code;

データモデル名に続けて {...} で囲われたブロック内では、対象データモデルに対する集計方法を記述します。

集計対象のプロパティは「グループ化キー」で指定された値ごとにまとめられ、 行頭で指定する「集計関数」(ここの例では anysum )に従って集計されます。

models.dmdl
"カテゴリ別売上集計"
summarized category_summary = joined_sales_info => {

    "カテゴリコード"
    any category_code -> category_code;

    "販売数量"
    sum amount -> amount_total;

    "売上合計"
    sum selling_price -> selling_price_total;
} % category_code;

sum amount -> amount_total; となっている行を例に説明すると、

  • 集計前のデータモデル joined_sales_info が持つプロパティ amount
  • 集計関数 sum によって合計して
  • 集計後のデータモデル category_summary が持つプロパティ amount_total という名前で利用する

という意味になります。

集計関数 sum は、グループ化した中の値の合計を算出します。 集計対象のプロパティは数値を表すデータ型を指定する必要があります。 また、値には NULL を含むことができないことに注意してください。

集計関数 any は、集計処理を行わずにデータモデルの値をそのまま利用します。 any はグループ化キーなど、集計が不要な項目で、グルーピングした結果にすべて同じ値が入っているようなプロパティに使用します。 この例では any を指定している category_code はグループ化キーです。

最下行の % category_code ではグループ化キーとなるプロパティを指定しています。 マッピング前後でプロパティ名を変更する場合、このプロパティ名は 集計結果のプロパティ名 を指定する必要があることに注意してください。

models.dmdl
"カテゴリ別売上集計"
summarized category_summary = joined_sales_info => {

    "カテゴリコード"
    any category_code -> category_code;

    "販売数量"
    sum amount -> amount_total;

    "売上合計"
    sum selling_price -> selling_price_total;
} % category_code;

9.3. 終わりに

全てのデータモデルを定義した後にデータモデルクラスの生成を行い、6つのデータモデルの生成が成功していることをコンソールで確認してください。

このチュートリアル終了時点のDMDLスクリプト models.dmdl は、次のようになります。

models.dmdl
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
"売上明細"
sales_detail = {

    "売上日時"
    sales_date_time : DATETIME;

    "店舗コード"
    store_code : TEXT;

    "商品コード"
    item_code : TEXT;

    "数量"
    amount : INT;

    "販売単価"
    unit_selling_price : INT;

    "販売金額"
    selling_price : INT;
};

"店舗マスタ"
store_info = {

    "店舗コード"
    store_code : TEXT;

    "店舗名称"
    store_name : TEXT;
};

"商品マスタ"
item_info = {

    "商品コード"
    item_code : TEXT;

    "商品名"
    item_name : TEXT;

    "商品部門コード"
    department_code : TEXT;

    "商品部門名"
    department_name : TEXT;

    "商品カテゴリコード"
    category_code : TEXT;

    "商品カテゴリ名"
    category_name : TEXT;

    "商品単価"
    unit_selling_price : INT;

    "マスタ登録日"
    registered_date : DATE;

    "マスタ適用開始日"
    begin_date : DATE;

    "マスタ適用終了日"
    end_date : DATE;
};

"売上明細+商品マスタ"
joined joined_sales_info
= sales_detail -> {

    "商品コード"
    item_code -> item_code;

    "販売数量"
    amount -> amount;

    "売上合計"
    selling_price -> selling_price;
} % item_code
+ item_info -> {

    "商品コード"
    item_code -> item_code;

    "カテゴリコード"
    category_code -> category_code;
} % item_code;

"カテゴリ別売上集計"
summarized category_summary = joined_sales_info => {

    "カテゴリコード"
    any category_code -> category_code;

    "販売数量"
    sum amount -> amount_total;

    "売上合計"
    sum selling_price -> selling_price_total;
} % category_code;

"エラー情報"
error_record = {

    "ファイル名"
    file_name : TEXT;

    "売上日時"
    sales_date_time : DATETIME;

    "店舗コード"
    store_code : TEXT;

    "商品コード"
    item_code : TEXT;

    "エラーメッセージ"
    message : TEXT;
};