Excel

Excel, VBA, Power Query, Power Pivot

Excel 3MIS (3 Months In Service) 計算

1. 前置き

某Tier1がサプライヤーの品質評価に3MISという指標を使っているらしいです? 一般的なワイブルチャートの高さと傾きを簡易的に評価する指標かも知れません?(間違っているのかも知れません?)
ネット検索によるとSP-3MISは、Supplier Performance 3 Months In Servecの略で、エンドユーザーに渡ってから3ヵ月以内に発生した同一納入月の部品の不良率らしいです?

これをExcelで算出する方法を考えてみました。但し、品質の作り込みは納入時では無く生産時ですので、部品の生産日ベースの不良率にしました。某Tier1の基準は納入月だと思うので、Tier1のSP-3MISとは異なります。

納車後3ヵ月経過していない場合は値が小さくなるので、納車後3ヵ月前未満は計算しないようにしました。でも、部品の生産日ベースだと、仮に納車開始から3ヵ月経過したとしても、そのロットが全て納車済みということはありません。なので納車日ベースにしない限り、必ず初期の3MISは小さく出ることに注意してください。

実際の不良は少ないので、月毎の変動が大きくなります。そこで、3ヵ月移動平均も算出しました。単純に移動平均を計算すると生産終了後の月も計算してしまいます。ピボットテーブルにする場合、小計や総計に3ヵ月移動平均を表示する意味が無いです。これらを表示しない式にしたので、計算要否判定の式が長くなりました。

もっと良い方法があるかも知れません。検証を殆どしていないので、使用される場合は式に間違いがないかを確認し、検証をお願いいたします。

2. 入力

1) 部品生産データ

年月ごとの生産数の表です。
ファイル名 : 生管*.xlsx(*は任意の文字)

◢ A B C D E F 省略 AY AZ
1 生管数量     24/03 24/04 24/05   28/02 28/03
2 顧客 機種 項目 24/03 24/04 24/05   28/02 28/03
3 A a 生産数     100   100  
4 B b 生産数            
5 A a 納入数     100   100  
6 B b 納入数            
  • 1行目のyy/mmは、Excelの日付のシリアル値です。これは、集計に使いません。
  • 2行目のyy/mmは、=TEXT(D1, "yy/mm") のテキストです。これを集計に使用します。
  • 項目の納入数は、本例では使用しません。納入年月ベースで3MISを集計する場合の予備行です。
  • 同じ形のシートであれば、複数のファイルに分かれていてもOKです。但し、1ファイル1シートにしてください。重複が無いようにしてください。普通は1年分が1ファイルになっているかと思います。
  • 本例では顧客を使用していません。使用する場合は、顧客ディメンジョンテーブルを作成して、リレーションしてください。

2) 市場サービスデータ

本例では.csvのテキストファイル(UTF-8)とします。Excelで開いた場合のレイアウトを次に示します。
ファイル名 : Service*.csv(*は任意の文字)

◢ A B C D E F G
1 市場不良            
2 発生日 顧客 機種 納車日 納入日 生産日 数量
3 2024/4/21 A a 2024/4/21 2024/4/11 2024/4/1 1
4 2024/5/21 A a 2024/5/21 2024/5/11 2024/5/1 1
5 2024/6/21 A a 2024/6/21 2024/6/11 2024/6/1 1
  途中省略            
117 2028/1/21 A a 2025/1/21 2025/1/11 2025/1/1 1
118 2028/2/21 A a 2025/2/21 2025/2/11 2025/2/1 1
119 2028/3/21 A a 2025/3/21 2025/3/11 2025/3/1 1

本例では、顧客と機種と生産日を'生管'テーブルと合わせていますが、実際に入手できるデータは異なるので、整形処理をしてください。この時、元データを直接修正するのではなく、Power Queryで整形してください。

  • 不要な行を削除してください。
  • 部品生産データと同じ顧客列を追加してください。
  • 部品生産データと同じ機種列を追加してください。または、列を追加せずに、変換テーブルをディメンジョンテーブルにしてPower Pivotでリレーションで参照しても良いです。
  • 製番から生産日の列を追加してください。
  • 同じ形の.csvあれば、複数のファイルに分かれていてもOKです。重複が無いようにしてください。普通は1月分が1ファイルになっていると思います。

3) xMISデータ

集計ブックにテーブルで作成します。
'xMIS'テーブル

xMIS xMISid
03MIS 3
06MIS 6
12MIS 12
24MIS 24
36MIS 36
合計 999
  • xMISが集計する月数をxMISidで指定します。
  • xMISid=999は、全期間の合計の判断に使用します。メジャーの中で使用します。
  • この表を変更する場合は、後述の計算列のDAX式も変更が必要です。

4) 機種データ

集計ブックにテーブルで作成します。部品生産データと市場サービスデータの対応を取るためのディメンジョンテーブルにします。
'機種'テーブル

顧客 機種 上市月数
A a 0
A b 1
  • 機種の重複が無いようにしてください。(機種をリレーションのキーとします。)
  • 顧客と機種は、Power Queryで整形後の部品生産データと市場サービスデータと同じにします。
  • 上市月数は、部品の生産日から納車日までの月数を入れてください。これは、評価可能な経過月数に達していない場合に、不良数や不良率を計算しないことにより、ピボットグラフの線を表示しないようにするためのものです。実際のこの月数はバラツキますが、何時から不良率を表示するのかという観点で決めます。不良率の精度を重視するなら大きな値を入れ、表示時期の早さを重視するなら小さな値を入れます。

5) 締日

集計ブックにテーブルで作成します。
'締日'テーブル

日付
2028/3/31
  • 最新の市場実績日を入力します。
    不良が無い場合に、0件という不良データが無くても、この日付まで市場不良が0件の実績があったとして、xMISの計算要否を決めます。
  • これより新しい日付の市場不良データがあれば、その日をxMISの計算要否の判断に使用します。

3. 出力

ピボットテーブル

機種 xMIS 値 24/1Q 24/1Q 集計
24/04 24/05 24/06
a 03MIS M不良率 1.00% 1.00% 1.00% 1.00%
M不良率3 1.00% 1.00% 1.00%  
06MIS M不良率 2.00% 2.00% 2.00% 2.00%
M不良率3 2.00% 2.00% 2.00%  
12MIS M不良率 2.00% 2.00% 2.00% 2.00%
M不良率3 3.00% 3.00% 3.00%  
24MIS M不良率 4.00% 4.00% 4.00% 4.00%
M不良率3 4.00% 4.00% 4.00%  
36MIS M不良率 5.00% 5.00% 5.00% 5.00%
M不良率3 5.00% 5.00% 5.00%  
合計 M不良率 6.00% 6.00% 6.00% 6.00%
M不良率3 6.00% 6.00% 6.00%  
  • 不良率の小計は、その期間の平均不良率です。
  • 不良率3は、3ヵ月移動平均です。3ヵ月移動平均不良率 = 3ヵ月の不良数 ÷ 3ヵ月の生産数

ピボットグラフ

260823mis1 260823mis2

4. 手順

1-1) ファクトテーブルの元データがあるフォルダの取得

  1. xMIS集計ブックを開き、[データ]-[データの取得v]-[ファイルから>]-[フォルダーから]で取得するファイルがあるフォルダを選択し、[開く]-[データの変換]でPower Queryエディタに取込み、右の名前を'フォルダ'に変更する。 ⇒ フォルダの下のサブフォルダも含むファイルリストができる。
  2. [Name]列の▼-[テキストフィルター>]-[次の値で始まらない]-[詳細設定]-[句の追加]で次のように入力し[OK]。 ⇒ ~$から始まる作業ファイルや集計を含む集計ファイルが消える。
および/または 列 演算子 値 解説
  Name 次の値で始まらない ~$ 作業ファイルを除く
および Name 指定の値を含まない mis 集計ファイルを除く
および Name 指定の値を含まない 集計 集計ファイルを除く

1-2) 生管(クロス表)部品生産データの取得と整形と結合

  1. 左の'フォルダ'クエリを右クリックし[参照]、右の名前を'生管'に変更。 ⇒ '生管'クエリが生成される。
  2. [Name]列の▼-[テキストフィルター>]-[指定の値を含む]-[詳細設定]で次のように入力し[OK]。 ⇒ 部品生産ファイルに絞り込む。
および/または 列 演算子 値 解説
  Name 指定の値を含む 生管 部品生産ファイルに絞り込む
および Extension 指定の値に等しい .xlsx Excelブックに絞り込む
  1. [Content]列を右クリックし[他の列の削除]。 ⇒ ファイルの内容以外の列が削除される。

  2. [Content]列の↓↓をクリックし、フォルダアイコンを選択し[OK]。 ⇒ 1つのファイルを処理するためのヘルパークエリが別に生成され、複数のファイルが縦に並ぶ。ファイルの内容は未展開。

  3. 右に生成された'サンプルファイル'、'ファイルの変換'、サンプル ファイルの変換'クエリの名前の後ろに"生管"を追加する。 ⇒ この作業は必須では無いが、名前で何を処理するヘルパークエリかが判別できるようになる。次に1ファイルを整形をするヘルパークエリを編集する。

  4. 'サンプル ファイルの変換生管'クエリを選択し、[KIND]列の▼-[テキストフィルター>]-[指定の値に等しい]で次のように入力し[OK]。 ⇒ Sheetに絞り込む。(印刷範囲などの情報を除く)

列 演算子 値 解説
Kind 指定の値に等しい Sheet Sheet以外を除く
  1. [Data]列を右クリックし[他の列の削除]。 ⇒ ファイルの内容以外の列が削除される。
  2. [Data]列の矢印アイコン(展開)をクリックし、"元の列名をプレフィックスとして使用します"のチェックを外し[OK]。 ⇒ ファイルの内容が展開される。(複数のシートがある場合は、単純に縦結合される。)
  3. [Column3]の▼-[空の削除]。 ⇒ 項目列の項目が無い不要な行が消え、1行目が列項目名になる。
  4. [変換]-[1行目をヘッダーとして使用v]-[1行目をヘッダーとして使用]。 ⇒ 1行目がヘッダになる。
  5. 自動生成された"変更された型"のステップを削除。
  6. yy/mm形式の年月以外の列をCtrlキーを押しながら全て選択し、[変換]-[列のピボット解除v]-[その他の列のピボット解除]。 ⇒ yy/mm列が縦1列に並び、リスト形式の表になる。
  7. [項目]列を選択し、[変換]-[列のピボット]で次のように入力し[OK]。 ⇒ 異なる値の種類を持つ項目が横に分離される。
    値列 : [値]
    詳細設定オプション-値の集計関数 : [集計しない]

※ もし列のピボットでエラーが出る場合は、列のピボット解除の前のクロス表の状態でnullを0に置換してください。

  1. [列の追加]-[カスタム列]で次のように入力し[OK]。 ⇒ [日付]列が追加される。
新しい列名 : 日付
カスタム列の式 : =
#date(
2000 + Number.FromText(Text.BeforeDelimiter([属性], "/")),
Number.FromText(Text.AfterDelimiter([属性], "/")),
1
)

解説 : yy/mm形式のテキストからPower Queryの日付形式の列を作ります。

  1. [属性]列を右クリックし[削除]。 ⇒ 不要なyy/mm列が削除される。1ファイルを整形する関数クエリが完成する。次にファイルを結合するクエリを編集する。

  2. 左の"生管"クエリを選択し、エラーになっている右の最後の"変更された型"のステップを削除する。

  3. 中央の任意のセルを選択しCtrl+aで全列を選択し、[変換]-[データ型の検出]。型が正しくない列名の左の型アイコンをクリックし、適切な型を選び、[現在のものを置換]。 ⇒ 正しい方が設定される。【注意】原則としてany型(ABC123)を残さないこと。残すと例えば数字の1と文字列の1が異なるものとして処理される。

1-3) 市場(リスト)市場サービスデータの取得と整形と結合

  1. 左の'フォルダ'クエリを右クリックし[参照]、右の名前を'市場'に変更。 ⇒ '市場'クエリが生成される。
  2. [Name]列の▼-[テキストフィルター>]-[指定の値を含む]-[詳細設定]で次のように入力し[OK]。 ⇒ 市場ファイルに絞り込む。
および/または 列 演算子 値 解説
  Name 指定の値を含む Service 市場ファイルの名前に絞り込む
および Extension 指定の値に等しい .csv .csvファイルに絞り込む
  1. [Content]列を右クリックし[他の列の削除]。 ⇒ 内容以外の列が削除される。

  2. [Content]列の↓↓(ファイルの結合)をクリックし、[OK]。 ⇒ 1つのファイルを処理するためのヘルパークエリが別に生成され、複数のファイルが結合される。

  3. 右に生成された'サンプルファイル'、'ファイルの変換'、サンプル ファイルの変換'クエリの名前の後ろに"市場"を追加する。

  4. 'サンプル ファイルの変換市場'クエリを選択する。(もし、右に"ソース"と"ナビゲーション"以外のステップがあれば、そのステップを削除する。 ⇒ 自動生成された不要なステップが削除される。)

  5. [ホーム]-[行の削除v]-[上位の行の削除]で行数に1を入れ[OK]。 ⇒ 不要な1行目が消える。

  6. [変換]-[1行目をヘッダーとして使用v]-[1行目をヘッダーとして使用]。 ⇒ ヘッダが設定される。1ファイルを整形する関数クエリが生成される。次にファイルを結合するクエリを編集する。(もし、"変更された型"が自動生成されたら、適切な型を設定し直すか、"変更された型"のステップを削除する。)

  7. 左の"市場"クエリを選択し、右の最後の"変更された型"のステップを削除する。

  8. 中央の任意のセルを選択しCtrl+aで全列を選択し、[変換]-[データ型の検出]。型が正しくない列名の左の型アイコンをクリックし、適切な型を選び、[現在のものを置換]。 ⇒ 正しい型が設定される。

  9. [ホーム]-[閉じて読み込むv]-[閉じて次に読み込む...]で、"接続の作成のみ"を選び[OK]でExcelに戻ります。

  10. "クエリと接続"ペインの'生管'と'市場'クエリをそれぞれ右クリックし、[読み込み先...]で"このデータをデータモデルに追加する"を選択し、[OK]。 ⇒ データモデルに追加される。

1-4) 各種設定テーブルの取得

  1. xMISのテーブルの1つのセルを選択し、[データ]-[テーブルまたは範囲から]でPower Queryエディタに取込む。
  2. 型が正しくない列名の左の型アイコンをクリックし、適切な型を選び、[現在のものを置換]。 ⇒ 正しい型が設定される。(日付の型は、時刻が無いものにすする)
  3. [ホーム]-[閉じて読み込むv]-[閉じて次に読み込む]で"接続の作成のみ"と"このデータをデータモデルに追加する"を選択し[OK]でExcelに戻る。 ⇒ データモデルに追加される。

同様の手順で、'締日'と'機種'テーブルをデータモデルに追加する。

1-5) 日付テーブルの生成

Excelの場合は、Power Query Mで日付テーブルを生成します。(ワークシートで作成し、データモデルに追加しても良いです。)

  1. [データ]-[データの取得v]-[その他のデータソースから>]-[空のクエリ]でPower Queryエディタに空のクエリを生成し、右の名前を'日付'に変更します。
  2. [ホーム]-[詳細エディタ]で次のコードを上書きし、[OK]。
let
Source1 = 市場, // [日付]列を持つクエリ名を設定
Source2 = 生管, // [日付]列を持つクエリ名を設定
    // ── 集計対象期間の決定 ──────────────
MinDate = Date.StartOfMonth(List.Min({List.Min(Source1[生産日]), List.Min(Source2[日付])})),
MaxDate = Date.EndOfMonth(List.Max({List.Max(Source1[発生日]), List.Max(Source2[日付]), List.Max(締日[日付])})), // 年度末日まで生成しない場合の式
// MaxDateRaw = List.Max({List.Max(Source1[日付]), List.Max(Source2[日付])}), // 年度末日まで生成する場合の式
// FiscalYearEndYear = if Date.Month(MaxDateRaw) >= 4 then Date.Year(MaxDateRaw) + 1 else Date.Year(MaxDateRaw),
// MaxDate = #date(FiscalYearEndYear, 3, 31),
    // ── 日単位カレンダーの生成 ──────────────────────
DateList = List.Dates(MinDate, Duration.Days(MaxDate - MinDate) + 1, #duration(1, 0, 0, 0)),
CalendarTable = Table.FromList(DateList, Splitter.SplitByNothing(), {"日付"}),
型設定済 = Table.TransformColumnTypes(CalendarTable, {{"日付", type date}}),
    // ── 会計年度(4月始まり)の基準日・年・月を1回だけ算出 ──
// EDATE([@日付], -3) 相当:3ヶ月前にずらすことで
// 会計年度の「年」と「月番号(4月=1~翌3月=12)」を取得
会計基準日追加 = Table.AddColumn(型設定済, "会計基準日", each Date.AddMonths([日付], -3), type date),
会計年追加 = Table.AddColumn(会計基準日追加, "会計年", each Date.Year([会計基準日]), Int64.Type),
会計月追加 = Table.AddColumn(会計年追加, "会計月", each Date.Month([会計基準日]), Int64.Type),
    // ── 表示用列の追加 ──────────────────────────────
    // 年:YEAR([@日付])
列_年 = Table.AddColumn(会計月追加, "年", each Date.Year([日付]), Int64.Type),
    // 月:TEXT([@日付],"m月") ※ゼロ埋めなし
列_月 = Table.AddColumn(列_年, "月", each Text.From(Date.Month([日付])) & "月", type text),
    // 年月:TEXT([@日付],"yy/mm") ※ゼロ埋めあり
列_年月 = Table.AddColumn(列_月, "年月", each
let
yy = Text.PadStart(Text.From(Date.Year([日付]) - 2000), 2, "0"),
mm = Text.PadStart(Text.From(Date.Month([日付])), 2, "0")
in
yy & "/" & mm,
type text),
    // 四半期.:INT((MONTH(x)-1)/3)+1 & "Q" ※年度なし
列_四半期ドット = Table.AddColumn(列_年月, "四半期.", each
let
四半期番号 = Number.IntegerDivide([会計月] - 1, 3) + 1
in
Text.From(四半期番号) & "Q",
type text),
    // 四半期:(YEAR(x)-2000) & "/" & 四半期番号 & "Q" ※ゼロ埋めなし
列_四半期 = Table.AddColumn(列_四半期ドット, "四半期", each
let
四半期番号 = Number.IntegerDivide([会計月] - 1, 3) + 1
in
Text.From([会計年] - 2000) & "/" & Text.From(四半期番号) & "Q",
type text),
    // 半期:(YEAR(x)-2000) & "/" & 半期番号 & "H" ※ゼロ埋めなし
列_半期 = Table.AddColumn(列_四半期, "半期", each
let
半期番号 = Number.IntegerDivide([会計月] - 1, 6) + 1
in
Text.From([会計年] - 2000) & "/" & Text.From(半期番号) & "H",
type text),
    // 期:IF(MONTH(x)<7,"上期","下期") ※年度なし
列_期 = Table.AddColumn(列_半期, "期", each
if [会計月] < 7 then "上期" else "下期",
type text),
    // 年度:(YEAR(x)-2000) & "年度" ※ゼロ埋めなし
列_年度 = Table.AddColumn(列_期, "年度", each
Text.From([会計年] - 2000) & "年度",
type text),
    // 年度.:YEAR(x) ※西暦そのまま、年度なし表示との組み合わせ用
列_年度ドット = Table.AddColumn(列_年度, "年度.", each [会計年], Int64.Type),
    // 内部計算用の一時列を削除し、ご指定の列順に並べ替え
不要列削除済 = Table.RemoveColumns(列_年度ドット, {"会計基準日", "会計年", "会計月"}),
最終結果 = Table.ReorderColumns(不要列削除済,
{"日付", "年月", "四半期", "半期", "年度", "年度.", "四半期.", "期", "月", "年"})
in
最終結果

補足 : このクエリは'生管'と'市場'と'締日'クエリの日付の範囲の日付テーブルを生成します。年度始まりを4月としています。最初のSource1とSource2とMaxDateの行にクエリ名を設定します。

参考 : CalendarTableまでのコードを詳細エディタで作成し、型設定済からをPower Queryエディタの主にカスタム列の追加を使用して作成することもできます。

  1. [ホーム]-[閉じて読み込むv]-[閉じて次に読み込む...]で、"接続の作成のみ"と"このデータをデータモデルに追加する"を選び[OK]でExcelに戻ります。 ⇒ データモデルに追加される。

2) '市場'データモデルに[xMISid]列を追加(計算列)

  1. 次のいずれかの方法でデータモデルを表示します。
    a. [Power Pivot]-[管理]-[ホーム]-[表示]-[データビュー]。
    b. [データ]-[データモデルv(緑アイコン)]-[データモデルの管理]-[ホーム]-[表示]-[データビュー]。
  2. 下の'市場'タブを選択します。
  3. 列項目の[列の追加]をWクリックし名前を"xMISid"に変更します。
  4. 上の数式バーに次のコードを入力し、✓をクリックします。(数式バーの右端のアイコと下端のドラッグで表示行数を広げることができます。)
=VAR m=DATEDIFF('市場'[納車日], '市場'[発生日], DAY) / 30
RETURN
SWITCH(
TRUE,
m<=3, 3,
m<=6, 6,
m<=12, 12,
m<=24, 24,
m<=36, 36,
999
)

解説 : 1ヶ月を30日として月数を計算します。DATEDIFFで納車日から発生日までの日数を求め、/30で月数に換算しています。月の長さをSWITCH関数で各MISの月数に区分分けしています。このSWITCHの第1引数をTRUEにする使い方はDAX固有の使い方で、ワークシート関数のIFS()に似ています。999は月数では無く合計の識別子として使用します。

参考

DAXのDATEDIFF(,,MONTH)は、月またぎの回数を月数とするので、3MISの計算には使用できません。
Excelのワークシート関数DATEDIF(,,"M")は、開始日と同日に達した回数を月数とするので、3MISの計算には使用できません。

参考

  • 追加した列のことを計算列と呼びます。計算列の式は、DAX式です。メジャーのDAX式と異なり、ピボットテーブルのコンテキストの影響を受けません。直接参照する列名には行コンテキストが掛かり、同一行の値を示します。なのでメジャーと異なり集計関数が不要です。関数内の列名は、行コンテキストが無く表全体を指します。

  • 計算列は、Power Queryのカスタム列の追加に似ています。

  • Power Query MにはDATEDIFF/DATEDIFに相当する関数が無いので、本記事では計算列を使用しています。Power Query Mで実装したい場合は、DATEDIFに相当する関数クエリを使います。ネットで検索すると、関数クエリが見つかります。

3) リレーションの作成

次のいずれかの方法でリレーションを設定します。
a. [Power Pivot]-[管理]-[ホーム]-[表示]-[ダイアグラムビュー]。
b. [データ]-[データモデルv(緑アイコン)]-[データモデルの管理]-[ホーム]-[表示]-[ダイアグラムビュー]。
c. [データ]-[データモデルv(緑アイコン)]-[リレーションシップ]-[編集]。

ファクトテーブル *--<--1 ディメンジョンテーブル(関連)
'生管'[日付] *--<--1 '日付'[日付]
'市場'[生産日] *--<--1 '日付'[日付]
'生管'[機種] *--<--1 '科目'[機種]
'市場'[機種] *--<--1 '科目'[機種]
'市場'[xMISid] *--<--1 'xMIS'[xMISid]
'締日'[日付] : リレーションを取らない

ダイアグラムビューでは、(多)から1へ項目をドラッグする。
リレーションシップでは、上段が(多)で下段の関連が1になるようにテーブルと項目を設定する。

参考 : このデータ構造は2つのスタースキーマが合体した構造で、コンステレーションスキーマ(Constellation Schema)と呼ばれるらしいです。 By Gemini

3-1) ピボットテーブルの生成

[挿入]-[ピボットテーブルv]-[データモデルから]で、ピボットテーブルを生成します。

3-2) メジャーの作成

  1. 次のいずれかの方法でメジャーを作成します。
    a. ① ピボットテーブルを選択し、右に"ピボットテーブルのフィールド"を表示。② メジャーを作成するテーブル('予算'または'実績)を右クリックし[メジャーの追加]。
    b. [Power Pivot]-[管理]でメジャーを書くテーブルの下の空白セルを選択。数式バーにメジャーを入力する。
    c. [データ]-[データモデル(緑アイコン)v]-[データモデルの管理]で、メジャーを書くテーブルの下の空白セルを選択。数式バーにメジャーを入力する。

解説 : メジャーの計算範囲と計算条件

  1. xMIS制限
    納車から3ヵ月以下では、6MISと3MISは同じ値になります。しかし、これは6MISの値が良い訳ではなく、6MISの評価期間を満了していないためです。なので、誤解を避けるため6MISの値をBLANKにする式を入れています。
  2. 最終生産月制限
    最終生産月の翌月の3ヵ月移動平均不良率は、翌月のデータが無いので2ヶ月分で計算されます。しかし、生産が無い月の不良率は意味が無いので、これをBLANKにする式を入れています。
  3. 小計・総計
    3ヵ月移動平均不良率の小計・総計はその区間の直近月の値と同じになり、小計・総計区間の不良率ではありません。誤解を避けるため、これをBLANKにする式を入れています。なお、小計・総計区間の不良率は、単月の不良率のメジャーで計算されます。
メジャー 期間 xMIS制限 最終生産月制限 小計・総計
M不良数 現在のコンテキスト あり 生産数で判定 表示
M不良率 現在のコンテキスト ― ― 表示
M不良数3 直近3か月 あり あり 非表示
M生産数3 直近3か月 ― あり 非表示
M不良率3 直近3か月 ― あり 非表示

'生管'に作るメジャー

M生産数3:=VAR CurrentDate = MAX('日付'[日付])	// 年月コンテキストの日付の最大値(実質月末日になる)
VAR LastProductionDate = EOMONTH(
CALCULATE(MAX('生管'[日付]), ALL('日付')),
0
) // コンテキストの機種における生産実績の最終月末日
RETURN
IF(
HASONEVALUE('日付'[年月]) // 単月コンテキストのみ計算
&&
CurrentDate <= LastProductionDate, // 最終生産月以前を計算
    CALCULATE(	// 直近3か月の生産数を集計
SUM('生管'[生産数]),
DATESINPERIOD('日付'[日付], CurrentDate, -3, MONTH)
)
)

解説 : 3ヵ月移動平均不良率の分母となる3ヵ月間の生産数を計算します。CULCULATE関数によりコンテキストをDATESINPERIOD関数の範囲に変更し、そのコンテキストの合計をSUM関数で求めています。第3引数(偽の式)が無いIF()は、偽の時に何も返しません。BLANK()と同じ処理になります。
長い式ですが、生産数の計算をしているのは、最後のCALUCULATE関数だけで、その他は、計算の要否を判定する式です。

 

'市場'に作るメジャー

M不良数:=VAR CurrentMIS = MAX('xMIS'[xMISid])	// コンテキストのxMIS
VAR LastServiceDate = MAX(
CALCULATE(MAX('市場'[発生日]), ALL('市場')),
MAX('締日'[日付])
) // 最新市場不良有効日
RETURN
IF(
CurrentMIS = 999 // 合計は計算
||
IF(
MIN('日付'[日付]) <= LastServiceDate, // 計算開始日が最新市場不良有効日以前の場合のみ処理
// (最新市場不良有効日 ~ 計算開始日)の月越え数 - 上市月数 >= xMISの期間 なら計算。小計も計算するので計算開始日で判定
DATEDIFF(MIN('日付'[日付]), LastServiceDate, MONTH) - MAX('機種'[上市月数]) >= CurrentMIS
),
    CALCULATE(	// コンテキストのxMIS以下の不良数を合計
SUM('市場'[数量]),
FILTER(ALL('xMIS'), 'xMIS'[xMISid] <= CurrentMIS)
) + 0 // 不良が無い場合は0
)

解説 : これはコンテキストの期間の不良数の合計です。xMISは下位のxMISを含むので、FILTER関数でxMISidが小さい下位の範囲を含むコンテキストに変更してSUM関数で合計します。
xMISの有効性は、(最新市場不良有効日 - 計算開始日)の月数 - 上市月数 >= コンテキストのxMISの月数 が成立する条件で判断しています。(最新市場不良有効日 - 計算開始日)の月数は、DATEDIFF(,,MONTH)で計算される月またぎの回数です。不良率は小計も計算しますが、小計区間に1つでもxMISが有効な月が含まれていれば、小計を出すためにコンテキストの開始日で有効性を判断します。
例 : 「最新市場不良有効日3月 - 計算開始日1月 - 上市月数0」は、「3-1-0=2 < 3MISの月数3」なので計算しない。即ち、1月は3月時点では2ヶ月経過で、3MISの3ヵ月を満たしていないので、3MISを計算しません。
長い式ですが、不良数の計算をしているのは、最後のCALUCULATE関数だけで、その他は、計算の要否を判定する式です。

 

M不良率:=DIVIDE([M不良数], SUM('生管'[生産数]))

解説 : コンテキストの期間の不良率。DIVIDEは割り算をする関数で、分母が0の時エラーを表示しません。実務では分子を1,000,000倍して、ppmにした方が良いです。

 

M不良数3:=VAR CurrentDate = MAX('日付'[日付])	// 年月コンテキストの日付の最大値(実質月末日になる)
VAR CurrentMIS = MAX('xMIS'[xMISid]) // コンテキストのxMISid
VAR LastProductionDate = EOMONTH(
CALCULATE(MAX('生管'[日付]), ALL('日付')),
0
) // コンテキストの機種における生産実績の最終月末日
VAR LastServiceDate = MAX(
CALCULATE(MAX('市場'[発生日]), ALL('市場')),
MAX('締日'[日付])
) // 最新市場不良有効日
RETURN
IF(
HASONEVALUE('日付'[年月]) // 単月コンテキストのみ計算
&&
CurrentDate <= LastProductionDate // 最終生産月以前を計算
&&
( // xMISの有効性の判定
CurrentMIS = 999 // 合計は計算
||
IF(
CurrentDate <= LastServiceDate, // 計算終了日が最新市場不良有効日以前の場合のみ処理
// (最新市場不良有効日 ~ 計算終了日)の月越え数 - 上市月数 >= xMISの期間 なら計算。移動平均は単月コンテキストのみ計算するので計算終了日で判定
DATEDIFF(CurrentDate, LastServiceDate, MONTH) - MAX('機種'[上市月数]) >= CurrentMIS
)
),
    CALCULATE(
SUM('市場'[数量]), // 3ヶ月間の不良数を合計
FILTER(ALL('xMIS'), 'xMIS'[xMISid] <= CurrentMIS), // 下位xMISを含む
DATESINPERIOD('日付'[日付], CurrentDate, -3, MONTH) // 当月を含む3ヶ月間
) + 0 // 不良が無い場合は0
)

解説 : 3ヵ月の移動平均不良率の分子となる3ヵ月の不良数の合計。式の仕組みは、[M生産数3]の3ヵ月の処理と部分と[M不良数]の下位xMISの処理を組み合わせたものです。これに、最終生産月制限を加えています。
xMISの有効性判定は計算終了日を用いています。これは、移動平均が単月コンテキストしか計算しないので、小計のコンテキストの開始日が当月より前になることに配慮する必要が無いからです。
その他は、式中のコメントを参照してください。
長い式ですが、不良数の計算をしているのは、最後のCALUCULATE関数だけで、その他は、計算の要否を判定する式です。

 

M不良率3:=DIVIDE([M不良数3], [M生産数3])

解説 : 3ヵ月の移動平均不良率。割り算の分母である[M生産数3]がBLANKなら、BLANKを返すので、計算するかどうかの判断は[M生産数3]の中のみで良い。

12) ピボットテーブルの設定

デバッグ用の集計一覧

デバッグ用に不良率以外の値も表示するピボットテーブルを作成します。報告書などには、不良率のみ表示したピボットテーブル、ピボットグラフを作成してください。

  1. 次のように項目を設定します。
  • 列 : '日付'[四半期]、'日付'[年月]。
  • 行 : '機種'[機種]、'xMIS'[xMIS]、Σ値。
  • Σ値 : '生管'[生産数]、[M不良数]、[M不良率]、[M生産数3]、[M不良数3]、[M不良率3]。
  1. [ピボットテーブル分析]-[オプション]-[レイアウトと書式]で、"更新時に列幅を調整する"のチェックを外す。
  2. [ピボットテーブル分析]-[オプション]-[表示]で、"データのないアイテムを行/列に表示する"のチェックを外す。
  3. [デザイン]-[レポートのレイアウト]-[アウトライン形式で表示]または[表形式]。(表形式だと小計が下に表示される)
  4. [デザイン]-[総計v]-[行と列の集計を行う]。
  5. 表の"四半期"を右クリックし、["四半期"の小計]をクリックして✓マークを付ける。
  6. [デザイン]-[ピボットテーブルスタイル]で、ベースとするスタイルを右クリックし[複製]。ユーザー設定のスタイルを右クリックし、[変更]でスタイルを変更。[デザイン]-[ピボットテーブルスタイル]で、ユーザー設定したスタイルをクリック。
  • 最初の列 : 右罫線
  • 見出し行 : 下罫線; 網掛け
  • 小計列 1 : 右計算、網掛け
  • 小計行 1 : 上下罫線
  • 列小見出し 1 : 上罫線(細)
  • 列小見出し 2 : 上罫線(細)
  • 行小見出し 1 : 上罫線(細)
  • 行小見出し 2 : 上罫線(細)
  • 総計列 : 右罫線(太); 網掛け
  • 総計行 : 上下罫線(細,太); 網掛け

参考 : ピボットテーブルのスタイルの自由度は低いです。手動でセルの書式設定しても、更新で書式がクリアされる場合があります。自由に書式設定したい場合は、GETPIVOTDATAを使用して別のワークシート表で書式設定したほうが良いです。

ピボットグラフ用のピボットテーブルとピボットグラフの作成

  1. 集計一覧のピボットテーブルがあるシートをコピーします。
  2. 次のように項目を設定します。
  • フィルター : '機種'[機種]
  • 列 : 'xMIS'[xMIS]、Σ値。
  • 行 : '日付'[年月]。
  • Σ値 : [M不良率] または [M不良率3]。
  1. [ピボットテーブル分析]-[オプション]-[表示]で、"データのないアイテムを行/列に表示する"のチェックを外す。

  2. [デザイン]-[レポートのレイアウト]-[表形式]。

  3. [デザイン]-[総計v]-[行と列の集計を行わない]。

  4. 表の"四半期"を右クリックし、["四半期"の小計]をクリックして✓マークを外す。

  5. [ピボットテーブル分析]-[ピボットグラフ]-[折れ線]-[マーカー付折れ線]-[OK]

  6. 機種フィルターで1機種を選択

別条件のピボットテーブルとピボットグラフの作成

  1. 前記ピボットテーブルとピボットグラフがあるシートをコピー。または、ピボットテーブルとピボットグラフ全体を含むセル範囲を切り取り、別の場所に貼り付ける。
  2. 機種フィルターやΣ値の項目を変更。

5. 注意

不良品の製番の製造日がリワークなどの入庫ずれなどで、生産数のデータの製造日と合わず、不良品の製番から求めた製造日の生産数が0の場合、不良率は表示されません。後述のサンプルファイルの2025年5月の機種aがその例です。
これにより、サンプルの単月の推移グラフの時間軸から2025年5月が抜け落ちていますが、グラフでは気づきません。
3ヵ月移動平均は、3ヶ月間生産0が続いていないのでグラフに表示されまが、3ヶ月間生産0が続けば単月と同じことが起きます。

この間違いに気づく為に、サンプルではデバッグ用の'一覧シート'を設けています。これを見ると25/05の不良数が1なのに、生産数が空白であることから、間違いに気づきます。

ピボットテーブルの[オプション]-[表示]で"データのないアイテムを行/列に表示する"のチェックを入れると、データが無いことに気づきやすくなりますが、余計な項目も表示されます。この場合は、メジャーで余計な項目の非表示処理をするより、ピボットテーブルのフィルターやスライサーやワークシートの非表示機能で不要な部分を非表示にした方が、短いメジャーになります。

この例以外の事も含め間違いに気づく為には、メジャーのDAX式の動作を理解していることが必要です。メジャーは、コンテキストを理解していないと想定外の値を出すことがあるので、コンテキストを認識してください。

本メジャーの作成にChatGPTを利用しましたが、AI/LLMの式は抜けや冗長な部分が多くあり、かなりの手修正を入れています。コンテキストが分からない時に、AI/LLMを活用することは有効です。その際は、入力と出力の項目とリレーションとメジャーをインプットします。

6. サンプル

ダウンロード - 260823_3mis.zip

C:\Users\Public\Documents\260823_3mis に展開した各ファイルを置いてください。

  • 集計1.xlsx : 3MISの集計ブックです。
  • 生管.xlsx : 部品の生産データです。
  • Service.csv : 市場サービスデータです。UTF-8です。
  • Service.xlsx : テストデータService.csvを作成するための元データです。集計.xlsxでは使いません。
  • 月数テスト.xlsx : 月数の計算式のテストをしたファイルです。

7. 参考

| | コメント (0)

Google Looker Studio と Microsoft Power BI

セルフサービス型のBIツールであるLooker StudioとPower BIの比較表をGeminiに作成してもらいました。

機能/要素 Microsoft Power BI Google Looker Studio
主な用途 複雑なデータモデリング、深掘り分析 Web/マーケティングダッシュボード、迅速な共有
ETL / 前処理 Power Query(超強力) 限定的(データ結合、簡易変換のみ)
計算エンジン DAX(非常に高度な記述が可能) 計算フィールド(SQL/Excelライクな関数)
ツリーマップ ✅ 標準搭載 ✅ 標準搭載
分解ツリー ✅ 標準搭載(AIビジュアル) ❌ 非対応(ドリルダウンで代替)
導入・共有の容易さ Desktopアプリ + Proライセンス必要 ブラウザのみで完結、URL共有が容易

Looker Studioにメジャーやイテレート関数は無いようですが、計算フィールドやテーブル計算を用いて、似たようなことはできるらしいです。メジャーを知らない人には、こちらの方が良いかも知れません。でも、メジャーを知っている人は、逆にその知識が邪魔になるかも知れません?

Excelユーザーにとっては、Power Query、DAXの知識が活かせるPower BIの方が敷居が低そうです。
でもPower BI Proは別料金なので、Microsoft365に組み込まれない限り、普及は進まない気がします。
結果を貼れるのはWeb版のPower Pointのみで、OfficeのOLEを利用できないもの難点です。これはクラウドサービスであるLooker Studioも同じです。
Power BIのビジュアルの設定のUIが、Officeとは大きく異なる点も、Officeユーザーへの普及の妨げになるでしょう。

Excel Power Pivotでもダッシュボードやドリルダウンはできますが、ユーザーが多いExcelにPower BIのビジュアルと解析ツールが搭載されたほうが、Power BIより喜ぶ人が多そうな気がします。Excel Power Pivotの方が、Power BIより使う機会が多いです。

個人的には、今更Spread SheetやLooker Studioを学習する気は全く無いのですが、気になりますね。

久しぶりにPower BI Desktopを開いたら、Power Apps for Power BIとPower Automate for Power BIのビジュアルが追加されていました。これらは、ビジュアルを作るものではなく、データの操作やアクションをするもののようです。ビジュアルの作成は、Deneb (Vega / Vega-Lite ビジュアル)、Power BI Developer Tools を使うらしいです。

Power BIの悪いところ

  1. UIがMicrosoft Officeと異なるので、直感で設定箇所が分からないです。
  2. Power BI Desktopのレポートビューで編集のためにオブジェクトを右クリックしても、編集コンテキストメニューが出ず、ビジュアルの書式設定のメニュー階層を下る必要があり、分かりにくいです。また、ビジュアルの書式設定の表示領域が狭く、図も無く文字だけで機能を判断できないことにも問題があります。このUIがOfficeと比べて分かりにくい点です。
    ⇒ 編集モードと閲覧モードを分け、右クリックのコンテキストメニューや書式設定画面をOfficeと同じにすれば、分かりやすくなると思います。

Office開発チームがPower BI開発チームを吸収した方が、開発効率が上がり、統一感が出てユーザーが余計なものを覚えなくても良いようになると思います。ExcelにPower BIをアドオンし、Graph専用のシートのようにPower BIのページを持たせればよいと思います。ExcelのシートにPower BIのレポートページを追加するのは、なかなか良いアイデアだと思うのですが...誰かが特許を出しているのでしょうかね?

| | コメント (0)

Excel/Power BI 損益計算書(PL)の作成

1. 前置き

A. Excelのワークシート式で損益を計算

Excelで損益計算をするには、
① Power Queryで売上や会計等の各データの取得・整形・結合を行い、
② Power Pivotのピボットテーブルで科目別の合計を計算し、
③ GETPIVOTDATA関数でワークシートに取込み、ワークシート式で損益を計算するのが分かりやすくて良いと思います。
次の動画でその例が紹介されています。

ピボットテーブルは、合計が可能な同種の数値を、行と列項目のグループ別に集計する表です。行と列項目のグループとは別の、損益などの小計同士の計算や数量・率・人数等の異種の数値を表に入れることができません。なので、金額のグループ集計をピボットテーブルで行い、その結果を参照してワークシートで損益計算をすることをお勧めします。

本記事の「2.入力」に示すデータを使用した場合のリレーションとメジャーとピボットテーブルとワークシート式は、次の画像と「6.サンプル」を見てください。データの取得手順は、「4.手順」を見てください。GETPIVOTDATAは=の後にピボットテーブルをクリックすると自動生成されるので、変数のみ手修正します。そして、類似のセルにコピーしてスピルの範囲を修正すれば、入力の手間を少なくできます。

260815qpws1 260815qpws2


一方、ピボットテーブルとワークシートで似たような表が2つあるのは、無駄に感じるかも知れません。そこで、強引にメジャーやPower Queryで損益を計算して、ピボットテーブルに損益を表示する2つの方法が次の動画で紹介されています。

B. メジャーで損益を計算

  • 損益計算書(P&L)、貸借対照表(バランスシート)の作成方法 -071 : Power BIちゃんねる : 2024/08/23
    https://www.youtube.com/watch?v=MAAvOSNdGl4
    1期間の実算と予算を含む総勘定元帳をマトリックス(ピボットテーブル)で集計します。

この動画では元データは1期間の「General Ledger」1つです。

【本記事で紹介する内容】
実務では勘定科目や部門や商品等にデータが分かれていることがあります。また、複数期間の推移を見ます。そこで、本記事では、この方法を用いて複数期間を含む元データが科目分類ごとに分かれている手順を紹介します。

この方法の利点は、Excelのピボットテーブルのフィルター/スライサー/項目追加や、Power BIの分解ツリー/ドリルダウン/ドリルスルーにより、動的に集計範囲を変えることにより、損益偏差の要因調査の補助ツールになることです。なので、提出用ではなく、損益の調査分析用に用いると良いです。Power BI Desktopは、無料で分解ツリーなどを利用できます。しかし、他人との共有は有料契約が必要なので、他人に見せるならExcelが良いです。本記事では、Excelの操作方法を紹介します。

  • Power BI Desktop(無料版) : Microsoft
    https://www.microsoft.com/ja-jp/power-platform/products/power-bi/desktop
    Power BIでもExcelのPower QueryとDAXのコードを使用できます。Power Queryエディタの画面は、Excelとほぼ同じです。Power Pvot for Excelでは、メジャーの名前と式の間が":="ですが、Power BIでは"="になります。

C. Power Queryで損益を計算

  • 【Excel】ピボットテーブルで総勘定元帳から損益計算書(PL)を作るなんて簡単。そんなふうに考えていた時期が俺にもありました。【YT0156】 : ExcelドカタCH : 2024/02/17
    https://www.youtube.com/watch?v=pjhkmd3qdL0
    複数期間別の総勘定元帳を集計します。

Power Queryのピボットテーブルで損益計算書を作りたい人向けです。メジャーと異なり、損益の中に勘定科目の内訳が並びません。

いずれの方法もPower Queryでデータの取得・整形・結合を行う部分までは、ほぼ同じです。


2. 入力

リスト形式とクロス形式の表を例にします。また、勘定科目、部門、予算/実績でファイルが分かれているものを例にします。

1) ファイルの仕様一覧

ファイル名 内容 形式 ファイル名に含む文字 拡張子 シート名 有効データ以外がnullの列
売上予算*.xlsx 売上予算 クロス表 売上予算 .xlsx 売上予算 項目/D/Column4
売上実績*.xlsx 売上実績 リスト 売上実績 .xlsx 売上実績 売上/G/Column7
原価予算*.xlsx 売上原価予算 クロス表 原価予算 .xlsx 原価予算 勘定科目名称/B/Column2
原価実績*.xlsx 売上原価実績 クロス表 原価実績 .xlsx 原価実績 勘定科目名称/B/Column2
経費予算*.xlsx 販売費及び一般管理費予算 クロス表 経費予算 .xlsx 経費予算 勘定科目名称/B/Column2
経費実績*.xlsx 販売費及び一般管理費実績 クロス表 経費実績 .xlsx 経費実績 勘定科目名称/B/Column2
その他予算*.xlsx その他予算 クロス表 その他予算 .xlsx その他予算 勘定科目名称/B/Column2
その他実績*.xlsx その他実績 クロス表 その他実績 .xlsx その他実績 勘定科目名称/B/Column2
  • クロス表は、横軸が"yy/mm"形式の月毎に集計された表です。
  • ファイル名の*は、任意の部門名や商品名や年月等が入ります。
  • 各ファイル名は、同じフォルダ名の下に格納します。サブフォルダの下でも良いです。
  • ファイル名に含まれる文字と拡張子が一致するファイルを取得します。本例は.xlsxですが、.csvに変更することもできます。
  • シート名は、データがあるシート名です。
  • 有効なデータ以外がnullの列を、不要な行をフィルターで削除する列に使用します。

2) 売上予算

◢ A B C D E F G
1 売上予算            
2 勘定科目コード 勘定科目名称 品目 項目 26/04 26/05 26/06
3 4000 国内売上A 商品1 数量 20 15 20
4 4001 国内売上B 商品2 数量 20 15 15
5 4100 海外売上A 商品3 数量 20 20 25
6 4101 海外売上B 商品4 数量 20 20 25
7 4000 国内売上A 商品1 単価 50 50 50
8 4001 国内売上B 商品2 単価 60 60 70
9 4100 海外売上A 商品3 単価 70 70 70
10 4101 海外売上B 商品4 単価 80 80 70
11 4000 国内売上A 商品1 売上 1,000 750 1,000
12 4001 国内売上B 商品2 売上 1,200 900 1,050
13 4100 海外売上A 商品3 売上 1,400 1,400 1,750
14 4101 海外売上B 商品4 売上 1,600 1,600 1,750
  • クロス表。日付は"yy/mm"形式のテキスト。

3) 売上実績

◢ A B C D E F G
1 売上実績            
2 日付 勘定科目コード 勘定科目名称 品目 数量 単価 売上
3 26/04/01 4000 国内売上A 商品1 12 50 600
4 26/04/01 4001 国内売上B 商品2 20 60 1,200
5 26/04/30 4000 国内売上A 商品1 8 50 400
6 26/04/01 4100 海外売上A 商品3 20 70 1,400
7 26/04/01 4101 海外売上B 商品4 20 80 1,600
8 26/05/01 4000 国内売上A 商品1 15 50 750
9 26/05/01 4001 国内売上B 商品2 15 60 900
10 26/05/01 4100 海外売上A 商品3 20 70 1,400
11 26/05/01 4101 海外売上B 商品4 15 80 1,200
12 26/06/01 4000 国内売上A 商品1 20 50 1,000
13 26/06/01 4001 国内売上B 商品2 15 70 1,050
14 26/06/01 4100 海外売上A 商品3 25 70 1,750
15 26/06/01 4101 海外売上B 商品4 25 70 1,750
  • リスト形式。

4) 売上原価予算/実績

◢ A B C D E F G
1 原価予算            
2 勘定科目コード 勘定科目名称 品目 項目 26/04 26/05 26/06
3 4500 国内売上原価A 商品1 数量 20 15 20
4 4501 国内売上原価B 商品2 数量 20 15 15
5 4600 海外売上原価A 商品3 数量 20 20 25
6 4601 海外売上原価B 商品4 数量 20 20 25
7 4500 国内売上原価A 商品1 単価 25 25 25
8 4501 国内売上原価B 商品2 単価 30 30 35
9 4600 海外売上原価A 商品3 単価 35 35 35
10 4601 海外売上原価B 商品4 単価 40 40 35
11 4500 国内売上原価A 商品1 原価 500 375 500
12 4501 国内売上原価B 商品2 原価 600 450 525
13 4600 海外売上原価A 商品3 原価 700 700 875
14 4601 海外売上原価B 商品4 原価 800 800 875
  • クロス表。日付は"yy/mm"形式のテキスト。

5) 販売費及び一般管理費予算/実績

◢ A B C D E F
1 経費実績          
2 勘定科目コード 勘定科目名称 部門 26/04 26/05 26/06
3 5001 給与手当 営業 450 400 400
4 5100 広告宣伝費 営業 300 300 300
5 5200 旅費交通費 営業 300 300 300
6 5300 通信費 営業 100 100 100
7 5400 消耗品費 営業 50 50 50
8 5500 水道光熱費 営業 50 50 50
9 5600 地代家賃 営業 250 250 250
  • クロス表。日付は"yy/mm"形式のテキスト。

6) その他予算/実績

◢ A B C D E
1 その他予算        
2 勘定科目コード 勘定科目名称 26/04 26/05 26/06
3 6100 利息受取 50 50 50
4 6200 雑収入 50 50 50
5 7100 為替差損 50 50 50
  • クロス表。日付は"yy/mm"形式のテキスト。

参考 : 以上の例に品目、部門、適用などの内訳列を追加すると、より細かい偏差要因調査ができます。

7) 勘定大項目マスター

テーブル名 : 勘定大項目

勘定大項目 順番
売上 1
売上原価 2
売上総利益 3
販売費及び一般管理費 4
営業利益 5
営業外収益 6
営業外費用 7
経常利益 8
  • 損益計算書の大項目。表示用。リレーションを取らない。
  • 本例では損益計算書のブックにあるものとします。

8) 科目マスター

テーブル名 : 科目

勘定科目コード 勘定科目名称 勘定大項目
4000 国内売上A 売上
4001 国内売上B 売上
4100 海外売上A 売上
4101 海外売上B 売上
4500 国内売上原価A 売上原価
4501 国内売上原価B 売上原価
4600 海外売上原価A 売上原価
4601 海外売上原価B 売上原価
5001 給与手当 販売費及び一般管理費
5100 広告宣伝費 販売費及び一般管理費
5200 旅費交通費 販売費及び一般管理費
5300 通信費 販売費及び一般管理費
5400 消耗品費 販売費及び一般管理費
5500 水道光熱費 販売費及び一般管理費
5600 地代家賃 販売費及び一般管理費
6100 利息受取 営業外収益
6200 雑収入 営業外収益
7100 為替差損 営業外費用
  • 勘定科目と勘定大項目の関係を定義するテーブル。予算と実績および勘定大項目との関連付けをする。

参考 : 本例では7)と8)は、損益計算書のブックにあるものとします。実務ではマスターデータの一元化のため、別の共有ファイルにした方が良いです。

この他に日付テーブルが必要ですが、ExcelではPower Query、Power BIではDAXで生成します。

3. 出力(損益表)

Excelのピボットテーブル、Power BIのマトリックスで出力します。

勘定大項目 勘定科目名称 1Q 1Q M実績 1Q M予算 1Q M予実差
26/04 26/05 26/06
M実績 M予算 M予実差 M実績 M予算 M予実差 M実績 M予算 M予実差
- 売上 5,200 5,200 ±0 4,250 4,650 -400 5,550 5,550 ±0 15,000 15,400 -400
  海外売上A 1,400 1,400 ±0 1,400 1,400 ±0 1,750 1,750 ±0 4,550 4,550 ±0
  海外売上B 1,600 1,600 ±0 1,200 1,600 -400 1,750 1,750 ±0 4,550 4,950 -400
  国内売上A 1,000 1,000 ±0 750 750 ±0 1,000 1,000 ±0 2,750 2,750 ±0
  国内売上B 1,200 1,200 ±0 900 900 ±0 1,050 1,050 ±0 3,150 3,150 ±0
-売上原価 2,700 2,600 +100 2,125 2,325 -200 2,775 2,775 ±0 7,600 7,700 -100
  海外売上原価A 700 700 ±0 700 700 ±0 875 875 ±0 2,275 2,275 ±0
  海外売上原価B 800 800 ±0 600 800 -200 875 875 ±0 2,275 2,475 -200
  国内売上原価A 600 500 +100 375 375 ±0 500 500 ±0 1,475 1,375 +100
  国内売上原価B 600 600 ±0 450 450 ±0 525 525 ±0 1,575 1,575 ±0
+ 売上総利益 2,500 2,600 -100 2,125 2,325 -200 2,775 2,775 ±0 7,400 7,700 -300
- 販売費及び一般管理費 2,450 2,400 +50 2,360 2,400 -40 2,400 2,400 ±0 7,210 7,200 +10
  給与手当 850 800 +50 800 800 ±0 800 800 ±0 2,450 2,400 +50
  広告宣伝費 300 300 ±0 300 300 ±0 300 300 ±0 900 900 ±0
  消耗品費 100 100 ±0 100 100 ±0 100 100 ±0 300 300 ±0
  水道光熱費 100 100 ±0 100 100 ±0 100 100 ±0 300 300 ±0
  地代家賃 500 500 ±0 500 500 ±0 500 500 ±0 1,500 1,500 ±0
  通信費 200 200 ±0 200 200 ±0 200 200 ±0 600 600 ±0
  旅費交通費 400 400 ±0 360 400 -40 400 400 ±0 1,160 1,200 -40
+ 営業利益 50 200 -150 -235 -75 -160 375 375 ±0 190 500 -310
- 営業外収益 100 100 ±0 100 100 ±0 160 100 +60 360 300 +60
  雑収入 50 50 ±0 50 50 ±0 110 50 +60 210 150 +60
  利息受取 50 50 ±0 50 50 ±0 50 50 ±0 150 150 ±0
- 営業外費用 50 50 ±0 40 50 -10 50 50 ±0 140 150 -10
  為替差損 50 50 ±0 40 50 -10 50 50 ±0 140 150 -10
+ 経常利益 100 250 -150 -175 -25 -150 485 425 +60 410 650 -240
  • 勘定大項目の利益は折りたたみ、科目内訳を展開表示しないでおきます。

Power BIでは、予実差の調査のための分解ツリーを表示できます。

4. 手順

ファクトテーブルの元データは、少なくとも[日付]、[勘定科目コード]、[金額]列を持つリストに整形し、予算と実績に分けて縦結合します。

1) ファクトテーブルの元データがあるフォルダの取得

  1. [データ]-[データの取得v]-[ファイルから>]-[フォルダから]で取得するファイルがあるフォルダを選択し、[開く]-[データの変換]でPower Queryエディタに取込み、右の名前を'フォルダ'に変更する。 ⇒ フォルダの下のサブフォルダも含むファイルリストができる。
  2. [Name]列の▼-[テキストフィルター>]-[次の値で始まらない]-[詳細表示]で次のように入力し[OK]。 ⇒ ~$から始まる作業ファイルやPLを含む集計ファイルが消える。
および/または 列 演算子 値 解説
  Name 次の値で始まらない ~$ 作業ファイルを除く
および Name 指定の値を含まない PL 損益計算書ファイルを除く

2) 各勘定科目のデータファイルを取得し整形

■ 売上予算(クロス表)

  1. 左の'フォルダ'クエリを右クリックし[参照]、右の名前を'売上予算'に変更。 ⇒ '売上予算'クエリが生成される。
  2. [Name]列の▼-[テキストフィルター>]-[指定の値を含む]-[詳細表示]で次のように入力し[OK]。 ⇒ 売上予算ファイルに絞り込む。
および/または 列 演算子 値 解説
  Name 指定の値を含む 売上予算 売上予算ファイルに絞り込む
および Extension 指定の値に等しい .xlsx Excelブックに絞り込む
  1. [Content]列を右クリックし[他の列の削除]。 ⇒ ファイルの内容以外の列が削除される。

  2. [Content]列の↓↓をクリックし、データがあるシートを選択し[OK]。 ⇒ 1つのファイルを処理するためのヘルパークエリが別に生成され、複数のファイルが縦結合される。

  3. 右に生成された'サンプルファイル'、'ファイルの変換'、サンプル ファイルの変換'クエリの名前の後ろに"売上予算"を追加する。 ⇒ この作業は必須では無いが、名前で何を処理するヘルパークエリかが判別できるようになる。次に1ファイルを整形をするヘルパークエリを編集する。

  4. 'サンプル ファイルの変換売上予算'クエリを選択し、右の"ソース"と"ナビゲーション"以外のステップを削除し、"ナビゲーション"を選択する。 ⇒ 自動生成された不要なステップが削除される。

  5. [Column2]の▼-[空の削除]。 ⇒ 不要な行が消え、1行目が列項目名になる。

  6. [変換]-[1行目をヘッダーとして使用v]-[1行目をヘッダーとして使用]。 ⇒ 1行目がヘッダになる。

  7. yy/mm形式の年月以外の列をCtrlキーを押しながら全て選択し、[変換]-[列のピボット解除v]-[その他の列のピボット解除]。 ⇒ yy/mm列が縦1列に並び、リスト形式の表になる。

  8. [列の追加]-[カスタム列]で次のように入力し[OK]。 ⇒ [日付]列が追加される。

新しい列名 : 日付
カスタム列の式 : =
#date(
2000 + Number.FromText(Text.BeforeDelimiter([属性], "/")),
Number.FromText(Text.AfterDelimiter([属性], "/")),
1
)

解説 : yy/mm形式のテキストからPower Queryの日付形式の列を作ります。

  1. [属性]列を右クリックし[削除]。 ⇒ 不要なyy/mm列が削除される。

  2. [項目]列を選択し、[変換]-[列のピボット]で次のように入力し[OK]。 ⇒ 異なる値の種類を持つ項目が横に分離される。
    値列 : [値]
    詳細設定オプション-値の集計関数 : [集計しない]

  3. 集計する金額の列名([売上])をWクリックし、名前を"金額"に変更。 ⇒ 集計する値の列名が他のクエリと同じになる。1ファイルを整形する関数クエリが完成する。次にファイルを結合するクエリを編集する。

  4. 左の"売上予算"クエリを選択し、エラーになっている右の最後の"変更された型"のステップを削除する。

  5. 中央の任意のセルを選択しCtrl+aで全列を選択し、[変換]-[データ型の検出]。型が正しくない列名の左の型アイコンをクリックし、適切な型を選び、[現在のものを置換]。 ⇒ 正しい方が設定される。【注意】原則としてany型(ABC123)を残さないこと。残すと例えば数字の1と文字列の1が異なるものとして処理される。

同様の手順で、他のクロス表の'売上原価予算'、'売上原価実績'、'経費予算'、'経費実績'、'その他予算'、'その他実績'を取込み・整形・結合します。

※ もし列のピボットでエラーが出る場合は、ピボット解除の前に(=クロス表の状態)nullを0に置換してください。

■ 売上実績(リスト)

  1. 左の'フォルダ'クエリを右クリックし[参照]、右の名前を'売上実績'に変更。 ⇒ '売上実績'クエリが生成される。

  2. [Name]列の▼-[テキストフィルター>]-[指定の値を含む]-[詳細表示]で次のように入力し[OK]。 ⇒ 売上実績ファイルに絞り込む。

    および/または 列 演算子 値 解説
      Name 指定の値を含む 売上実績 売上実績ファイルに絞り込む
    および Extension 指定の値に等しい .xlsx Excelブックに絞り込む
  3. [Content]列を右クリックし[他の列の削除]。 ⇒ 内容以外の列が削除される。

  4. [Content]列の↓↓をクリックし、データがあるシートを選択し[OK]。 ⇒ 1つのファイルを処理するためのヘルパークエリが別に生成され、複数のファイルが結合される。

  5. 右に生成された'サンプルファイル'、'ファイルの変換'、サンプル ファイルの変換'クエリの名前の後ろに"売上実績"を追加する。 ⇒ この作業は必須では無いが、名前で何を処理するヘルパークエリかが判別できるようになる。次に1ファイルを整形をするヘルパークエリを編集する。

  6. 'サンプル ファイルの変換売上実績'クエリを選択し、右の"ソース"と"ナビゲーション"以外のステップを削除し、"ナビゲーション"を選択する。 ⇒ 自動生成された不要なステップが削除される。

  7. [Column7]の▼-[空の削除]。 ⇒ 不要な行が消える。

  8. [変換]-[1行目をヘッダーとして使用v]-[1行目をヘッダーとして使用]。 ⇒ ヘッダが設定される。

  9. yy/mm形式の年月以外の列をCtrlキーを押しながら全て選択し、[変換]-[列のピボット解除v]-[その他の列のピボット解除]。 ⇒ yy/mm列が縦1列に並ぶ。

  10. 集計する金額の列名([売上])をWクリックし、名前を"金額"に変更。 ⇒ 集計する値の列名が他のクエリと同じになる。1ファイルを整形する関数クエリが生成される。次にファイルを結合するクエリを編集する。

  11. 左の"売上実績"クエリを選択し、右の最後の"変更された型"のステップを削除する。

  12. 中央の任意のセルを選択しCtrl+aで全列を選択し、[変換]-[データ型の検出]。型が正しくない列名の左の型アイコンをクリックし、適切な型を選び、[現在のものを置換]。 ⇒ 正しい方が設定される。

3) 予算の結合

  1. 左の'売上予算'クエリを選択し、[ホーム]-[クエリの追加v]-[クエリを新規クエリとして追加]でクエリを生成し、右の名前を"予算"に変更する。
  2. [3つ以上のテーブル]を選択し、'原価予算'、'経費予算'、'その他予算'をそれぞれ選択して[追加]し、[OK]。 ⇒ 予算の勘定元帳が完成する。(ピボットテーブルの元データ(ファクトテーブル))

同様の手順で'実績'クエリを作成する。

余談 : 本記事ではヘルパークエリを8個作成していますが、シートの表の形や項目が同じであれば、シートを整形するヘルパークエリを手動で作成することで、ヘルパークエリを削減することができます。7.参考の「Power Queryで複数の表を結合する方法(まとめ)」を見てください。

4) 品目の生成

  1. 左の'予算'クエリを右クリックし[参照]で右の名前を"品目"に変更する。
  2. [ホーム]-[クエリの追加v]-[クエリの追加]で[実績]を選択し[OK]。 ⇒ 実績が追加される。
  3. [品目]列を右クリックし[他の列の削除]。 ⇒ 品目以外の列が消える。
  4. [品目]列を右クリックし[重複の削除]。 ⇒ 重複が消える。
  5. [品目]列の▼-[空白の削除]。 ⇒ 空白が消える。(ディメンジョンテーブル)

5) 部門の生成

  1. 左の'予算'クエリを右クリックし[参照]で右の名前を"部門"に変更する。
  2. [ホーム]-[クエリの追加v]-[クエリの追加]で[実績]を選択し[OK]。 ⇒ 実績が追加される。
  3. [部門]列を右クリックし[他の列の削除]。 ⇒ 品目以外の列が消える。
  4. [部門]列を右クリックし[重複の削除]。 ⇒ 重複が消える。
  5. [部門]列の▼-[空白の削除]。 ⇒ 空白が消える。(ディメンジョンテーブル)

余談 : 本例では予算と実績のファクトテーブルを分けているので、両者を関連付けるために'部門'、'品目'ディメンジョンテーブルを使用しているが、予算と実績を1つに結合すれば、これらのディメンジョンテーブルは不要になる。また、部門や品目別の絞り込みをしないのであれば、これらのディメンジョンテーブルは不要である。

6) クエリの保存とデータモデルの追加

  1. [ホーム]-[閉じて読み込むv]-[閉じて次に読み込む...]で、"接続の作成のみ"を選択し[OK]でExcelに戻る。
  2. "クエリと接続"ペインの'予算'クエリを右クリックし、[読み込み先...]で"このデータをデータモデルに追加する"を選択し、[OK]。 ⇒ データモデルに追加される。

同様の手順で'実績'、'品目'、'部門'をデータモデルに追加する。

7) 勘定大項目の取得

  1. 勘定大項目のテーブルの1つのセルを選択し、[データ]-[テーブルまたは範囲から]でPower Queryエディタに取込む。
  2. 型が正しくない列名の左の型アイコンをクリックし、適切な型を選び、[現在のものを置換]。 ⇒ 正しい型が設定される。
  3. [ホーム]-[閉じて読み込むv]-[閉じて次に読み込む]で"接続の作成のみ"と"このデータをデータモデルに追加する"を選択し[OK]でExcelに戻る。 ⇒ データモデルに追加される。

同様の手順で、'科目'テーブルをデータモデルに追加する。

8) 日付テーブルの生成

Excelの場合は、Power Query Mで日付テーブルを生成します。(ワークシートで作成し、データモデルに追加しても良いです。)

  1. [データ]-[データの取得v]-[その他のデータソースから]-[空のクエリ]でPower Queryエディタに空のクエリを生成し、右の名前を'日付'に変更します。
  2. [ホーム]-[詳細エディタ]で次のコードを上書きします。
let
Source1 = 予算, // [日付]列を持つクエリ名を設定
Source2 = 実績, // [日付]列を持つクエリ名を設定
    // ── 集計対象期間の決定 ── 年度末日まで ───────────
MinDate = Date.StartOfMonth(List.Min({List.Min(Source1[日付]), List.Min(Source2[日付])})),
MaxDateRaw = List.Max({List.Max(Source1[日付]), List.Max(Source2[日付])}),
FiscalYearEndYear = if Date.Month(MaxDateRaw) >= 4 then Date.Year(MaxDateRaw) + 1 else Date.Year(MaxDateRaw),
MaxDate = #date(FiscalYearEndYear, 3, 31),
    // ── 日単位カレンダーの生成 ──────────────────────
DateList = List.Dates(MinDate, Duration.Days(MaxDate - MinDate) + 1, #duration(1, 0, 0, 0)),
CalendarTable = Table.FromList(DateList, Splitter.SplitByNothing(), {"日付"}),
型設定済 = Table.TransformColumnTypes(CalendarTable, {{"日付", type date}}),
    // ── 会計年度(4月始まり)の基準日・年・月を1回だけ算出 ──
// EDATE([@日付], -3) 相当:3ヶ月前にずらすことで
// 会計年度の「年」と「月番号(4月=1~翌3月=12)」を取得
会計基準日追加 = Table.AddColumn(型設定済, "会計基準日", each Date.AddMonths([日付], -3), type date),
会計年追加 = Table.AddColumn(会計基準日追加, "会計年", each Date.Year([会計基準日]), Int64.Type),
会計月追加 = Table.AddColumn(会計年追加, "会計月", each Date.Month([会計基準日]), Int64.Type),
    // ── 表示用列の追加 ──────────────────────────────
    // 年:YEAR([@日付])
列_年 = Table.AddColumn(会計月追加, "年", each Date.Year([日付]), Int64.Type),
    // 月:TEXT([@日付],"m月") ※ゼロ埋めなし
列_月 = Table.AddColumn(列_年, "月", each Text.From(Date.Month([日付])) & "月", type text),
    // 年月:TEXT([@日付],"yy/mm") ※ゼロ埋めあり
列_年月 = Table.AddColumn(列_月, "年月", each
let
yy = Text.PadStart(Text.From(Date.Year([日付]) - 2000), 2, "0"),
mm = Text.PadStart(Text.From(Date.Month([日付])), 2, "0")
in
yy & "/" & mm,
type text),
    // 四半期.:INT((MONTH(x)-1)/3)+1 & "Q" ※年度なし
列_四半期ドット = Table.AddColumn(列_年月, "四半期.", each
let
四半期番号 = Number.IntegerDivide([会計月] - 1, 3) + 1
in
Text.From(四半期番号) & "Q",
type text),
    // 四半期:(YEAR(x)-2000) & "/" & 四半期番号 & "Q" ※ゼロ埋めなし
列_四半期 = Table.AddColumn(列_四半期ドット, "四半期", each
let
四半期番号 = Number.IntegerDivide([会計月] - 1, 3) + 1
in
Text.From([会計年] - 2000) & "/" & Text.From(四半期番号) & "Q",
type text),
    // 半期:(YEAR(x)-2000) & "/" & 半期番号 & "H" ※ゼロ埋めなし
列_半期 = Table.AddColumn(列_四半期, "半期", each
let
半期番号 = Number.IntegerDivide([会計月] - 1, 6) + 1
in
Text.From([会計年] - 2000) & "/" & Text.From(半期番号) & "H",
type text),
    // 期:IF(MONTH(x)<7,"上期","下期") ※年度なし
列_期 = Table.AddColumn(列_半期, "期", each
if [会計月] < 7 then "上期" else "下期",
type text),
    // 年度:(YEAR(x)-2000) & "年度" ※ゼロ埋めなし
列_年度 = Table.AddColumn(列_期, "年度", each
Text.From([会計年] - 2000) & "年度",
type text),
    // 年度.:YEAR(x) ※西暦そのまま、年度なし表示との組み合わせ用
列_年度ドット = Table.AddColumn(列_年度, "年度.", each [会計年], Int64.Type),
    // 内部計算用の一時列を削除し、ご指定の列順に並べ替え
不要列削除済 = Table.RemoveColumns(列_年度ドット, {"会計基準日", "会計年", "会計月"}),
最終結果 = Table.ReorderColumns(不要列削除済,
{"日付", "年月", "四半期", "半期", "年度", "年度.", "四半期.", "期", "月", "年"})
in
最終結果
 

補足 : このクエリは'予算'クエリと'実績'クエリの日付の範囲の日付テーブルを生成します。年度始まりを4月としています。最初のSource1とSource2にクエリ名を設定します。

  1. [ホーム]-[閉じて読み込むv]-[閉じて次に読み込む...]で、"接続の作成のみ"と"このデータをデータモデルに追加する"を選び[OK]でExcelに戻ります。 ⇒ データモデルに追加される。

Power BIの日付テーブル

Power Query Mでも生成できますが、DAXでテーブルを作成することもできます。

日付 =
VAR FiscalMonthEnd = 3 -- 3月決算(4月始まり)の場合は3を指定
VAR MinDate = MIN( MIN('予算'[日付]), MIN('実績'[日付]) )
VAR MaxDate = MAX( MAX('予算'[日付]), MAX('実績'[日付]) )
VAR StartDate = DATE(YEAR(MinDate), MONTH(MinDate), 1)
VAR EndDate = EOMONTH(MaxDate, 0)
VAR BaseCalendar = CALENDAR(StartDate, EndDate)
RETURN
ADDCOLUMNS(
BaseCalendar,
"年", YEAR([Date]),
"月", MONTH([Date]),
"月名", FORMAT([Date], "MM月"),
"四半期", "Q" & FORMAT(EDATE([Date], -FiscalMonthEnd), "Q"),
"曜日", FORMAT([Date], "aaaa"),
"年度", IF(MONTH([Date]) > FiscalMonthEnd, YEAR([Date]), YEAR([Date]) - 1),
"会計月番号", IF(MONTH([Date]) > FiscalMonthEnd, MONTH([Date]) - FiscalMonthEnd, MONTH([Date]) + 12 - FiscalMonthEnd)
)

9) リレーションの作成

次のいずれかの方法でリレーションを設定します。
a) [Power Pivot]-[管理]-[表示]-[ダイアグラムビュー]。
b) [データ]-[データモデルv(緑アイコン)]-[データモデルの管理]-[ホーム]-[表示]-[ダイアグラムビュー]。
c) [データ]-[データモデルv(緑アイコン)]-[リレーションシップ]-[編集]。

ファクトテーブル *--<--1 ディメンジョンテーブル(関連)
'予算'[日付] *--<--1 '日付'[日付]
'実績'[日付] *--<--1 '日付'[日付]
'予算'[勘定科目コード] *--<--1 '科目'[勘定科目コード]
'実績'[勘定科目コード] *--<--1 '科目'[勘定科目コード]
'予算'[品目] *--<--1 '品目'[品目]
'実績'[品目] *--<--1 '品目'[品目]
'予算'[部門] *--<--1 '部門'[部門]
'実績'[部門] *--<--1 '部門'[部門]

ダイアグラムビューでは、(多)から1へ項目をドラッグする。
リレーションシップでは、上段が
(多)で下段の関連が1になるようにテーブルと項目を設定する。

参考 : このデータ構造はスタースキーマの一種で、コンステレーションスキーマ(Constellation Schema)と呼ばれるらしいです。 By Gemini

10) ピボットテーブルの生成

[挿入]-[ピボットテーブルv]-[データモデルから]で、ピボットテーブルを生成します。

11) メジャーの作成

  1. 次のいずれかの方法でメジャーを作成します。
    a) ① ピボットテーブルを選択し、右に"ピボットテーブルのフィールド"を表示。② メジャーを作成するテーブル('予算'または'実績)を右クリックし[メジャーの追加]。
    b) [Power Pivot]-[管理]でメジャーを書くテーブルの下の空白セルを選択。数式バーにメジャーを入力する。
    c) [データ]-[データモデル(緑アイコン)v]-[データモデルの管理]で、メジャーを書くテーブルの下の空白セルを選択。数式バーにメジャーを入力する。

'予算'に作るメジャー

M売上予算:=CALCULATE(
SUM('予算'[金額]),
'科目'[勘定大項目] = "売上"
)

解説 : ピボットテーブルに展開する'勘定大項目'と'科目'や'予算'にリレーションがありません。よって、SUM('予算'[金額])だけでは'勘定大項目'のフィルタコンテキストが掛かりません。そこで、勘定大項目別のメジャーを作り、CALCULATE関数で勘定大項目に該当する'予算'[金額]を合計を計算します。
'科目'1-▶-*'予算'のリレーションはあるので、'科目'で'予算'をフィルターすることができます。
このメジャーはピボットテーブルには表示せず、M予算メジャーの中から参照します。
以下のメジャーも同じです。
M売上原価予算:=CALCULATE(
SUM('予算'[金額]),
'科目'[勘定大項目] = "売上原価"
)

M販管予算:=CALCULATE(
SUM('予算'[金額]),
'科目'[勘定大項目] = "販売費及び一般管理費"
)

M営業外収益予算:=CALCULATE(
SUM('予算'[金額]),
'科目'[勘定大項目] = "営業外収益"
)

M営業外費用予算:=CALCULATE(
SUM('予算'[金額]),
'科目'[勘定大項目] = "営業外費用"
)

M予算:=SWITCH(
MAX('勘定大項目'[勘定大項目]),
"売上", [M売上予算],
"売上原価", [M売上原価予算],
"売上総利益", [M売上予算] - [M売上原価予算],
"販売費及び一般管理費", [M販管予算],
"営業利益", [M売上予算] - [M売上原価予算] - [M販管予算],
"営業外収益", [M営業外収益予算],
"営業外費用", [M営業外費用予算],
"経常利益", [M売上予算] - [M売上原価予算] - [M販管予算] + [M営業外収益予算] - [M営業外費用予算],
BLANK()
)

解説 : ピボットテーブルのΣ値にこのメジャーを表示します。ピボットテーブルの行に配置した'勘定大項目'[勘定大項目]により、返すメジャーを切替えます。各損益は、この式の中で計算して返します。

参考 : 利益率等も計算して表示できますが、金額と書式を自動で分けることができません。数量や人数も同様です。なので、損益計算書にそのような値を表示をしたい場合は、GETPIVOTDATAでワークシートに損益計算書を作成した方が良いと思います。

'実績'に作るメジャー

M売上:=CALCULATE(
SUM('実績'[金額]),
'科目'[勘定大項目] = "売上"
)

M売上原価:=CALCULATE(
SUM('実績'[金額]),
'科目'[勘定大項目] = "売上原価"
)

M販管:=CALCULATE(
SUM('実績'[金額]),
'科目'[勘定大項目] = "販売費及び一般管理費"
)

M営業外収益:=CALCULATE(
SUM('実績'[金額]),
'科目'[勘定大項目] = "営業外収益"
)

M営業外費用:=CALCULATE(
SUM('実績'[金額]),
'科目'[勘定大項目] = "営業外費用"
)

M実績:=SWITCH(
MAX('勘定大項目'[勘定大項目]),
"売上", [M売上],
"売上原価", [M売上原価],
"売上総利益", [M売上] - [M売上原価],
"販売費及び一般管理費", [M販管],
"営業利益", [M売上] - [M売上原価] - [M販管],
"営業外収益", [M営業外収益],
"営業外費用", [M営業外費用],
"経常利益", [M売上] - [M売上原価] - [M販管] + [M営業外収益] - [M営業外費用],
BLANK()
)

M予実差:=[M実績] - [M予算]

12) ピボットテーブルの設定

  1. 次のように項目を設定します。
  • 列 : '日付'[四半期.]、'日付'[月]、Σ値。
  • 行 : '勘定大項目'[勘定大項目]、'科目'[勘定科目名称]。
  • Σ値 : [M実績]、[M予算]、[M予実差]。
  1. [ピボットテーブル分析]-[オプション]-[レイアウトと書式]で、"更新時に列幅を調整する"のチェックを外す。
  2. [ピボットテーブル分析]-[オプション]-[表示]で、"データのないアイテムを行/列に表示する"のチェックを外す。
  3. [デザイン]-[レポートのレイアウト]-[アウトライン形式で表示]。(表形式だと小計が下に表示される)
  4. [デザイン]-[総計v]-[行と列の集計を行わない]。
  5. 表の"勘定大項目"と"四半期."を右クリックし、["xxx"の小計]をクリックして✓マークを付ける。
  6. 売上総利益、営業利益、経常利益などの利益の行を折りたたむ。
  7. [デザイン]-[ピボットテーブルスタイル]で、ベースとするスタイルを右クリックし[複製]。ユーザー設定のスタイルを右クリックし、[変更]でスタイルを変更。
  • 最初の列 : 右罫線
  • 見出し行 : 下罫線; 網掛け
  • 小計列 1 : 網掛け
  • 小計行 1 : 上下罫線
  • 列小見出し 1 : 上罫線(太)
  • 列小見出し 2 : 上罫線(細)
  • 列小見出し 3 : 上罫線(細)
  • 行小見出し 1 : 下罫線; 網掛け

参考 : ピボットテーブルのスタイルの自由度は低いです。手動でセルの書式設定しても、更新で書式がクリアされる場合があります。自由に書式設定したい場合は、GETPIVOTDATAを使用して別のワークシート表で書式設定したほうが良いです。

5. 分析

Excel

ピボットテーブルの行に[品目]や[部門]を配置すると、品目別や部門別の予実差を確認することができます。フィルターやスライサーを活用して絞り込むこともできます。

Power BI

  1. 分解ツリーを配置し、次のように項目を設定します。
  • 分析 : [M予実差]
  • 説明 : '科目'[勘定科目名称]、'品目'[品目]または'部門'[部門]
  1. マトリックスで分析をしたいM予実差の値をクリックをし、M予実差の右の+をクリックすると"勘定科目名称"と値の内訳が展開されます。

6. サンプル

ダウンロード - 260815pl.zip

C:\Users\Public\Documents\260815pl に展開した各ファイルを置いてください。

  • PL集計.xlsx : B. 本記事の損益集計ファイル(ファイルが大きいのはPower BIの画像を貼っているためです。)
  • PL集計.pbix : B. 本記事の損益集計のPower BI Desktop版
  • PLqpws.xlsx : A. 本記事のデータをPower Pivotで集計し、ワークシート式で損益を計算したもの(おすすめ)
  • #PL集計.xlsx : C. 本記事のデータをQueryで損益計算したもの
  • 売上予算.xlsx、売上実績.xlsx、原価予算.xlsx、原価実績.xlsx、経費予算営業.xlsx、経費実績営業.xlsx、経費予算調達.xlsx、経費実績調達.xlsx、その他予算.xlsx、その他実績.xlsx : 上記損益集計に使用する勘定元データ
  • PLbi.xlsx : B. Power BIのYouTubeの損益動画のExcel版。損益計算のメジャーだけを知りたい場合は、これを見てください。

7. 参考

8. その他

  1. 速度
    正確に測定したわけではありませんが、①Power BI、②Excelメジャーで損益計算、③Excel Power Queryで損益計算、の順で速度が速いようです。Power BIは、更新時のCPU利用率が高く、マルチコアのCPUの利用効率が高いようです。DAXはExcelよりPower BIの方が新しいので、性能が良いのだと思われます。

| | コメント (0)

Excel Power Pivot 生産実績表と不良実績表から不良率の推移表を作成

1. 前置き

生産実績表と不良実績表をExcel Power Queryで取得し、Power Pivotで不良率の推移表を作成する方法を紹介します。

不良率と改善効果を見るための不良率の前月差の式は、次の通りです。

不良率 = 不良数 ÷ 生産数
不良率の前月差 = 当月の不良率 - 先月の不良率

ピボットテーブルで、任意または最新の不良率の高い順に不良項目を並び替える方法も紹介します。

注釈 : 式の一部に、AI(Claude、Copilot、Gemini、ChatGPT)を利用しました。

2. 入力

ここでは、生産実績表と不良実績表が各Excelブックの1つのシートにリスト形式のテーブル化されていない表を例に説明します。
これ以外の形式でも対応できます。その場合は、10. 参考のリンクを見てください。複数のリストでもクロス表でも、Power Queryで結合/整形処理することができます。

1) 生産実績表

◢ A B C
1 生産実績    
2 日付 製品 投入数
3 2026/04/02 製品A 100
4 2026/04/17 製品B 50
5 2026/05/02 製品A 80
6 2026/05/17 製品B 40
7 2026/06/02 製品A 100
8 2026/06/17 製品B 50

2) 不良実績表

◢ A B C D
1 不良実績      
2 日付 製品 不良項目 不良数
3 2026/04/02 製品A 作業不良 4
4 2026/04/02 製品A 部品不良 2
5 2026/04/02 製品A 加工不良 2
6 2026/04/17 製品B その他 1
7 2026/04/17 製品B 作業不良 4
8 2026/04/17 製品B 部品不良 3
9 2026/05/02 製品A 加工不良 1
10 2026/05/02 製品A その他 0
11 2026/05/02 製品A 作業不良 0
12 2026/05/17 製品B 部品不良 1
13 2026/05/17 製品B 加工不良 2
14 2026/05/17 製品B その他 1
15 2026/06/02 製品A 作業不良 1
16 2026/06/02 製品A 部品不良 2
17 2026/06/02 製品A 加工不良 1
18 2026/06/17 製品B その他 0
19 2026/06/17 製品B 作業不良 1
20 2026/06/17 製品B 部品不良 1

3. 出力

ピボットテーブルに出力します。ワークシート式を使用しないことにより、式にセルアドレスを使用せず、式の数が減り、セルアドレス間違いが減ります。

不良項目別不良率推移(不良項目を任意の列の不良率で並び替え)

不良項目 値 26年度 26年度 集計
1Q 1Q 集計
4月 5月 6月
部品不良 合計 / 投入数 150 120 150 420 420
合計 / 不良数 5 1 3 9 9
M不良率 3.3% 0.8% 2.0% 2.1% 2.1%
M不良率前月差 -- -2.5% +1.2% -- --
作業不良 合計 / 投入数 150 120 150 420 420
合計 / 不良数 8 0 2 10 10
M不良率 5.3% 0.0% 1.3% 2.4% 2.4%
M不良率前月差 -- -5.3% +1.3% -- --
加工不良 合計 / 投入数 150 120 150 420 420
合計 / 不良数 2 3 1 6 6
M不良率 1.3% 2.5% 0.7% 1.4% 1.4%
M不良率前月差 -- +1.2% -1.8% -- --
その他 合計 / 投入数 150 120 150 420 420
合計 / 不良数 1 1 0 2 2
M不良率 0.7% 0.8% 0.0% 0.5% 0.5%
M不良率前月差 -- +0.2% -0.8% -- --
全体の 合計 / 投入数 150 120 150 420 420
全体の 合計 / 不良数 16 5 6 27 27
全体の M不良率 10.7% 4.2% 4.0% 6.4% 6.4%
全体の M不良率前月差 -- -6.5% -0.2% -- --

製品別不良率推移

製品 値 26年度 26年度 集計
1Q 1Q 集計
4月 5月 6月
製品A 合計 / 投入数 100 80 100 280 280
合計 / 不良数 8 1 4 13 13
M不良率 8.0% 1.3% 4.0% 4.6% 4.6%
M不良率前月差 -- -6.8% +2.8% -- --
製品B 合計 / 投入数 50 40 50 140 140
合計 / 不良数 8 4 2 14 14
M不良率 16.0% 10.0% 4.0% 10.0% 10.0%
M不良率前月差 -- -6.0% -6.0% -- --
全体の 合計 / 投入数 150 120 150 420 420
全体の 合計 / 不良数 16 5 6 27 27
全体の M不良率 10.7% 4.2% 4.0% 6.4% 6.4%
全体の M不良率前月差 -- -6.5% -0.2% -- --

不良項目別不良率推移(最新月の不良率順位で自動並び替え)

C最新不良順位 不良項目 値 26年度 26年度 集計
1Q 1Q 集計
4月 5月 6月
1 部品不良 合計 / 不良数 5 1 3 9 9
M不良率 3.3% 0.8% 2.0% 2.1% 2.1%
M不良率前月差 -- -2.5% +1.2% -- --
2 作業不良 合計 / 不良数 8 0 2 10 10
M不良率 5.3% 0.0% 1.3% 2.4% 2.4%
M不良率前月差 -- -5.3% +1.3% -- --
3 加工不良 合計 / 不良数 2 3 1 6 6
M不良率 1.3% 2.5% 0.7% 1.4% 1.4%
M不良率前月差 -- +1.2% -1.8% -- --
4 その他 合計 / 不良数 1 1 0 2 2
M不良率 0.7% 0.8% 0.0% 0.5% 0.5%
M不良率前月差 -- +0.2% -0.8% -- --
全体の 合計 / 不良数 16 5 6 27 27
全体の M不良率 10.7% 4.2% 4.0% 6.4% 6.4%
全体の M不良率前月差 -- -6.5% -0.2% -- --

4. 手順

1) 生産実績表と不良実績表の取得

  1. 不良率を算出するための集計Excelブックを作成し、開きます。

  2. [データ]-[データの取得v]-[ファイルから>]-[Excelブックから]で不良実績表のブックを選択し、[インポート]で不良実績表のシートを選択し、[データの変換]でPower Queryエディタに取込みます。

  3. 右の名前を'不良実績'に変更します。

  4. 右の"適用したステップ"の"ソース"と"ナビゲーション"の次のステップを右クリックし、[最後まで削除]。 ⇒ 自動生成された不要なステップが削除される。

  5. [Column2]列の▼で[null]のチェックを外し[OK]。 ⇒ [Column2]列の不要な行が削除される。

  6. [変換]-[1行目をヘッダとして使用v]-[1行目をヘッダとして使用]。 ⇒ 1行目がヘッダになる。

  7. 自動的に"変更された型"のステップが追加されるので、適切でない型アイコン(列名の左側)をクリックし、適切な型を選択し、[現在のものを置換]。 ⇒ データ型が設定される。

  8. [ホーム]-[閉じて読み込むv]-[閉じて次に読み込む...]で、"接続の作成のみ"と"このデータをデータモデルに追加する"を選び[OK]でExcelに戻ります。 ⇒ データモデルに追加される。

  9. 不良実績表と同様の手順で生産実績表を取得し、データモデルに追加します。クエリの名前は、'生産実績'にします。

参考 : クエリの名前がデータモデルのテーブル名になります。

2) 製品テーブルの作成

不良実績と生産実績を関連付ける、製品テーブルを作成します。

  1. 右に"クエリと接続"が表示されていなければ、[データ]-[クエリと接続]をクリックして"クエリと接続"ペインを右に表示し、適当なクエリを右クリックし[編集]で、Power Queryエディタを開きます。
  2. 左の'生産実績'を右クリックし、[参照]で新しいクエリを作成し、名前を'製品'に変更します。
  3. [製品]列を右クリックし、[その他の列の削除]。 ⇒ [製品]以外の列が削除される。
  4. [製品]列を右クリックし、[重複の削除]。 ⇒ 重複のない製品リストになる。
  5. [ホーム]-[閉じて読み込むv]-[閉じて次に読み込む...]で、"接続の作成のみ"と"このデータをデータモデルに追加する"を選び[OK]でExcelに戻ります。 ⇒ データモデルに追加される。

3) 日付テーブルの作成

Power Pivotでは、DAX式を簡単にするため、日付テーブルを使うことをお勧めします。Excelのワークシート式で作る方法[*1]が理解しやすいですが、ここでは不良実績表と生産実績表に連動した日付を生成するPower Query Mのコードで作成します。コードの意味はコメントを見てください。Power Query Mの仕様は、Googleで調べてください。会計基準日追加のステップからは、Power Queryエディタのカスタム列の追加でも作成できます。

  1. [データ]-[データの取得v]-[その他のデータソースから]-[空のクエリ]でPower Queryエディタに空のクエリを生成し、右の名前を'日付'に変更します。
  2. [ホーム]-[詳細エディタ]で次のコードを上書きします。
let
Source1 = 不良実績, // [日付]列を持つクエリ名を設定
Source2 = 生産実績, // [日付]列を持つクエリ名を設定
    // ── 集計対象期間の決定 ── 年度末日まで ───────────
  MinDate = Date.StartOfMonth(List.Min({List.Min(Source1[日付]), List.Min(Source2[日付])})),
// MaxDate = Date.EndOfMonth(List.Max({List.Max(Source1[日付]), List.Max(Source2[日付])})), // 年度末日まで表示しない場合の式
MaxDateRaw = List.Max({List.Max(Source1[日付]), List.Max(Source2[日付])}),
FiscalYearEndYear = if Date.Month(MaxDateRaw) >= 4 then Date.Year(MaxDateRaw) + 1 else Date.Year(MaxDateRaw),
MaxDate = #date(FiscalYearEndYear, 3, 31),
    // ── 日単位カレンダーの生成 ──────────────────────
DateList = List.Dates(MinDate, Duration.Days(MaxDate - MinDate) + 1, #duration(1, 0, 0, 0)),
CalendarTable = Table.FromList(DateList, Splitter.SplitByNothing(), {"日付"}),
型設定済 = Table.TransformColumnTypes(CalendarTable, {{"日付", type date}}),
    // ── 会計年度(4月始まり)の基準日・年・月を1回だけ算出 ──
// EDATE([@日付], -3) 相当:3ヶ月前にずらすことで
// 会計年度の「年」と「月番号(4月=1~翌3月=12)」を取得
会計基準日追加 = Table.AddColumn(型設定済, "会計基準日", each Date.AddMonths([日付], -3), type date),
会計年追加 = Table.AddColumn(会計基準日追加, "会計年", each Date.Year([会計基準日]), Int64.Type),
会計月追加 = Table.AddColumn(会計年追加, "会計月", each Date.Month([会計基準日]), Int64.Type),
    // ── 表示用列の追加 ──────────────────────────────
    // 年:YEAR([@日付])
列_年 = Table.AddColumn(会計月追加, "年", each Date.Year([日付]), Int64.Type),
    // 月:TEXT([@日付],"m月") ※ゼロ埋めなし
列_月 = Table.AddColumn(列_年, "月", each Text.From(Date.Month([日付])) & "月", type text),
    // 年月:TEXT([@日付],"yy/mm") ※ゼロ埋めあり
列_年月 = Table.AddColumn(列_月, "年月", each
let
yy = Text.PadStart(Text.From(Date.Year([日付]) - 2000), 2, "0"),
mm = Text.PadStart(Text.From(Date.Month([日付])), 2, "0")
in
yy & "/" & mm,
type text),
    // 四半期.:INT((MONTH(x)-1)/3)+1 & "Q" ※年度なし
列_四半期ドット = Table.AddColumn(列_年月, "四半期.", each
let
四半期番号 = Number.IntegerDivide([会計月] - 1, 3) + 1
in
Text.From(四半期番号) & "Q",
type text),
    // 四半期:(YEAR(x)-2000) & "/" & 四半期番号 & "Q" ※ゼロ埋めなし
列_四半期 = Table.AddColumn(列_四半期ドット, "四半期", each
let
四半期番号 = Number.IntegerDivide([会計月] - 1, 3) + 1
in
Text.From([会計年] - 2000) & "/" & Text.From(四半期番号) & "Q",
type text),
    // 半期:(YEAR(x)-2000) & "/" & 半期番号 & "H" ※ゼロ埋めなし
列_半期 = Table.AddColumn(列_四半期, "半期", each
let
半期番号 = Number.IntegerDivide([会計月] - 1, 6) + 1
in
Text.From([会計年] - 2000) & "/" & Text.From(半期番号) & "H",
type text),
    // 期:IF(MONTH(x)<7,"上期","下期") ※年度なし
列_期 = Table.AddColumn(列_半期, "期", each
if [会計月] < 7 then "上期" else "下期",
type text),
    // 年度:(YEAR(x)-2000) & "年度" ※ゼロ埋めなし
列_年度 = Table.AddColumn(列_期, "年度", each
Text.From([会計年] - 2000) & "年度",
type text),
    // 年度.:YEAR(x) ※西暦そのまま、年度なし表示との組み合わせ用
列_年度ドット = Table.AddColumn(列_年度, "年度.", each [会計年], Int64.Type),

// 内部計算用の一時列を削除し、ご指定の列順に並べ替え
不要列削除済 = Table.RemoveColumns(列_年度ドット, {"会計基準日", "会計年", "会計月"}),
最終結果 = Table.ReorderColumns(不要列削除済,
{"日付", "年月", "四半期", "半期", "年度", "年度.", "四半期.", "期", "月", "年"})
in
最終結果

補足 : このクエリは'不良実績'クエリと'生産実績'クエリの日付の範囲の日付テーブルを生成します。年度始まりを4月としています。最初のSource1とSource2にクエリ名を設定しています。

  1. [ホーム]-[閉じて読み込むv]-[閉じて次に読み込む...]で、"接続の作成のみ"と"このデータをデータモデルに追加する"を選び[OK]でExcelに戻ります。 ⇒ データモデルに追加される。

参考 : 日付テーブルは、重複しない連続した日付を持つディメンジョンテーブルです。Power PivotやPower BIで日付テーブルを用いると、DAXのタイムインテリジェンス関数を使うことができます。

[*1] : Excelのワークシートで日付テーブルを作成する方法は、次の記事を見てください。

4) リレーションの作成

次のいずれかの方法でリレーションを設定します。

a) [Power Pivot]-[管理]-[表示]-[ダイアグラムビュー]。
b) [データ]-[データモデルv(緑アイコン)]-[データモデルの管理]-[ホーム]-[表示]-[ダイアグラムビュー]。
c) [データ]-[データモデルv(緑アイコン)]-[リレーションシップ]-[編集]。

リレーションの項目と方向('テーブル'[項目])

ファクトテーブル *--<--1 ディメンジョンテーブル(関連)
'不良実績'[日付] *--<--1 '日付'[日付]
'生産実績'[日付] *--<--1 '日付'[日付]
'不良実績'[製品] *--<--1 '製品'[製品]
'生産実績'[製品] *--<--1 '製品'[製品]

リレーションを設定する手順

ダイアグラムビューでは、*(多)から1へ項目をドラッグします。
リレーションシップでは、上段が*(多)で下段の関連が1になるようにテーブルと項目を設定します。

5) ピボットテーブルの生成

[挿入]-[ピボットテーブルv]-[データモデルから]で、ピボットテーブルを生成します。

6) メジャーの作成

  1. 次のいずれかの方法でメジャーを作成します。
    a) ① ピボットテーブルを選択し、右に"ピボットテーブルのフィールド"を表示。② メジャーを作成するテーブル('予算'または'実績)を右クリックし[メジャーの追加]。
    b) [Power Pivot]-[管理]でメジャーを書くテーブルの下の空白セルを選択。数式バーにメジャーを入力する。
    c) [データ]-[データモデル(緑アイコン)v]-[データモデルの管理]で、メジャーを書くテーブルの下の空白セルを選択。数式バーにメジャーを入力する。

生産実績に作成するメジャー

M投入数前月:=CALCULATE(
SUM('生産実績'[投入数]),
PREVIOUSMONTH('日付'[日付])
)

解説 : PREVIOUSMONTH('日付'[日付])で'日付'を前月の範囲にし、SUM('生産実績'[投入数])で投入数を合計する。
前年同期にする場合は、SAMEPERIODLASTYEAR()を使います。この関数はコンテキストに期間が従うので、月でも四半期でも年でも使えます。

不良実績に作成するメジャー

M不良率:=DIVIDE(SUM('不良実績'[不良数]), SUM('生産実績'[投入数]))

解説 : 不良率 = 不良数 ÷ 投入数 です。
M不良数前月:=CALCULATE(
SUM('不良実績'[不良数]),
PREVIOUSMONTH('日付'[日付])
)

解説 : M投入数前月と同じ計算の仕組みです。

 

M不良率前月:=DIVIDE([M不良数前月], [M投入数前月])

解説 : 不良率 = 不良数 ÷ 投入数 です。

 

M不良率前月差簡単:=[M不良率] - [M不良率前月]

解説 : 対前月不良率差 = 当月の不良率 - 前月の不良率 です。この式では、前月の不良率が無い最初の月でも、当月の不良率が無い最新月の翌月でも、無い不良率を0%として計算します。また四半期や年度などにもその期の前の1ヶ月間に対する差が表示されます。本記事では、これらの無効な値を表示しない、次のメジャーを使用しています。

 

M不良率前月差:=VAR MinProdDate = EOMONTH(CALCULATE( MIN('生産実績'[日付]), ALL('生産実績') ), 0)    // 生産実績の最初の月末日
VAR MaxProdDate = CALCULATE( MAX('生産実績'[日付]), ALL('生産実績') ) // 生産実績の最後の日
VAR CurMinDate = MIN('日付'[日付]) // コンテキストの始めの日
VAR CurMaxDate = MAX('日付'[日付]) // コンテキストの最後の日
RETURN
SWITCH(
TRUE(),
CurMaxDate <= MinProdDate, "--", // 初月(前月が存在しない月) → "--"
CurMinDate > MaxProdDate, BLANK(), // データ期間外(初月以外) → BLANK()
NOT ISFILTERED('日付'[月]), "--", // 単月でない(年度・四半期など) → "--"
[M不良率] - [M不良率前月] // 通常の前月差
)

解説 : 前月の不良率が無い最初の月と、当月の不良率が無い最新月の翌月と、単月ではない合計列の不良率の差を計算しない式です。生産実績の最初の月末日と最後の日と、現在のコンテキストの最初と最後の日を各VARで変数に格納し、それをSWITCH関数の条件で判別して、処理を分けています。

参考 : 四半期は前四半期差、半期は前期差、年度は前年度差にすることもできます。詳しくは、次の記事を参考にしてください。

余談 : 同じ式で、日、月、四半期、半期、年度の平均不良率を算出できます。メジャーはワークシート式に比べて式の数が少なく、パラメータにセルアドレスではなくテーブル名と列名を使用するので、パラメータが何を指しているのかが分かりやすく、可読性が良いです。

7) ピボットテーブルの設定

  1. 次のように項目を設定します。
  • 列 : '日付'[年度]、'日付'[四半期.]、'日付'[月]
  • 行 : '不良実績'[不良項目]、Σ値
  • Σ値 : '生産実績'[投入数]、'不良実績'[不良数]、[M不良率]、[M不良率前月差]
  1. [ピボットテーブル分析]-[オプション]-[レイアウトと書式]で、"更新時に列幅を調整する"のチェックを外す。
  2. [ピボットテーブル分析]-[オプション]-[表示]で、"データのないアイテムを行/列に表示する"のチェックを外す。(これをチェックする場合は、フィルターやスライサーで表示しないディメンジョン項目の値の選択を解除する。)
  3. [デザイン]-[レポートのレイアウト]-[表形式で表示]。
  4. [デザイン]-[総計v]-[列のみ集計を行う]。
  5. 小計を表示する列項目を右クリックし、["xxx"の小計]をクリックして✓マークを付ける。(四半期.、年度など)
  6. [ピボットテーブル分析]-[オプション]-[レイアウトと書式]で、レイアウトの"セルとラベルを結合して中央揃えにする"をチェック。
  7. [デザイン]-[ピボットテーブルスタイル]で、ベースとするスタイルを右クリックし[複製]。ユーザー設定のスタイルを右クリックし、[変更]でスタイルを変更。
  • 最初の列 : 右罫線
  • 見出し行 : 下罫線; 網掛け
  • 小計列 1 : 右罫線(太); 網掛け
  • 小計列 2 : 右罫線(細); 網掛け
  • 列小見出し 1 : 上罫線(太)
  • 列小見出し 2 : 上罫線(細)
  • 列小見出し 3 : 上罫線(細)
  • 総計行 : 上罫線(細); 網掛け
  1. 上記ピボットテーブルをコピーし、次のように項目を設定します。
  • 列 : '日付'[年度]、'日付'[四半期.]、'日付'[月]
  • 行 : '製品'[製品]、Σ値
  • Σ値 : '生産実績'[投入数]、'不良実績'[不良数]、[M不良率]、[M不良率前月差]

参考 : ピボットテーブルの設定は、データモデルに影響しないので、ピボットテーブルをコピーして別の設定をすることが可能です。一方テーブルは、データモデルそのものなので、コピーして別の設定にすることができません。

8-1) 不良率の高い不良項目の順に並び替え(半自動)

  1. 不良項目別不良率推移のピボットテーブルの不良項目の[▼]-[その他の並べ替えオプション...]で"降順"を選択し、[M不良率]を選択。
  2. [その他のオプション]で並べ替えで並び替えのキーに使用する列の[M不良率]の最初のセルを選択し[OK]。

260807defect1

8-2) 最新月の不良率の高い不良項目の順に並び替え(自動)

最新月の不良率の順位(降順)を'不良実績'テーブルに「計算列」で追加し、それをピボットテーブルの'不良実績'[不良項目]の上に配置します。

  1. [Power Pivot]-[管理]-'不良実績'で[列の追加]をWクリックし、列名を[M最新不良率順位]に変更する。
  2. 数式バーに次のDAX式を入力し[✓]をクリックする。
=
// ① '不良実績'テーブル全体(現在行のフィルタを無視)から最新の日付を取得
// ALLで計算列自身の行コンテキストによるコンテキスト変移を解除している
VAR vLatestDate =
CALCULATE ( MAX ( '不良実績'[日付] ), ALL ( '不良実績' ) )
// ② ①で求めた最新日付が、'日付'テーブルの「年月」列で何月に該当するかを取得
VAR vLatestYM =
LOOKUPVALUE ( '日付'[年月], '日付'[日付], vLatestDate )
// ③ 順位付けの母集団となる「不良項目」の一覧(重複なし、フィルタなし)を取得
VAR vDefectItems =
ALL ( '不良実績'[不良項目] )
RETURN
// ④ vDefectItemsの各不良項目を1つずつイテレートしながら、
// それぞれの不良率を計算し、その値で降順に順位付けする
RANKX (
vDefectItems,
        // --- ここから、RANKXが1不良項目ごとに評価する式 ---
        // イテレート中の対象不良項目(RANKXの行コンテキスト)を変数に保持
VAR vCurrentItem = '不良実績'[不良項目]
        // 最新年月(vLatestYM)かつ対象不良項目(vCurrentItem)に絞った不良数合計
// ALLで、外側(計算列)の行コンテキストによるコンテキスト変移を解除
VAR vDefectQty =
CALCULATE (
SUM ( '不良実績'[不良数] ),
ALL ( '不良実績' ),
'不良実績'[不良項目] = vCurrentItem,
'日付'[年月] = vLatestYM
)
        // 最新年月(vLatestYM)における投入数合計(製品・不良項目は問わない全体)
VAR vInputQty =
CALCULATE (
SUM ( '生産実績'[投入数] ),
ALL ( '生産実績' ),
'日付'[年月] = vLatestYM
)
        // 不良率 = 不良数合計 ÷ 投入数合計(0除算はDIVIDEでブランクを返す)
RETURN
DIVIDE ( vDefectQty, vInputQty ),
        // --- ここまでがRANKXの評価式(第2引数は省略=現在行の不良率を評価) ---
        ,        // 第3引数(並び順の詳細指定)は省略
DESC // 不良率が大きいほど順位が小さい数字(1位)になるよう降順で順位付け
)
  1. ピボットテーブルのフィールドで'不良実績'[C最新不良率順位]を行の'不良実績'[不良項目]の上に配置します。

参考 : 計算列もDAX式で記述します。計算列ではピボットテーブルのフィルターコンテキストは掛かりません。直接式に書かれた列名とコンテキストを変更する関数には、計算列の行コンテキストが掛かります。コンテキストを変更しない関数の中の引数は、コンテキストが掛かりません。

補足 : 計算列に順位を追加すると、ピボットテーブルの列/行に配置することで、自動的に順位で並び替えされます。一方、メジャーに順位を追加した場合は、Σ値に順位を表示し、それを列/行項目の並び替えのソースに設定します。メジャーだと、重複する順位表示が発生します。なので、計算列に順位を追加するのが良いです。Power Query Mが得意な人は、Power Queryで順位を追加しても良いです。

9. サンプル

ダウンロード - 260807defect.zip

C:\Users\Public\Documents\260807defect に展開した各ファイルを置いてください。

10. 参考

| | コメント (0)

Excel Power Pivot 入庫表と出庫表から在庫を算出

1. 前置き

入庫表と出庫表をExcel Power Queryで取得し、Power Pivotで在庫を算出する方法を紹介します。

在庫は次の式で求めます。

在庫 = 入庫累計 - 出庫累計

本記事は、次の記事をシンプルにしたものです。

関連記事

2. 入力

ここでは、入庫表と出庫表が各Excelブックの1つのシートにリスト形式のテーブル化されていない表を例に説明しますが、これ以外の形式でも対応できます。

1) 入庫表

◢ A B C
1 入庫    
2 日付 品名 入庫数
3 2026/04/01 品A 10
4 2026/04/16 品B 5
5 2026/05/01 品A 11
6 2026/05/16 品B 4
7 2026/05/31 品A 10
8 2026/06/15 品B 5
9 2026/06/30 品A 9

2) 出庫表

◢ A B C
1 出庫    
2 日付 品名 出庫数
3 2026/04/05 品A 8
4 2026/04/20 品B 4
5 2026/05/05 品A 11
6 2026/05/20 品B 4
7 2026/06/04 品A 10
8 2026/06/19 品B 5

3. 出力

ピボットテーブル

品名 値 26年度 26年度 集計
1Q 1Q 集計
4月 5月 6月
品A 合計 / 入庫数 10 21 9 40 40
合計 / 出庫数 8 11 10 29 29
M在庫 2 12 11 11 11
品B 合計 / 入庫数 5 4 5 14 14
合計 / 出庫数 4 4 5 13 13
M在庫 1 1 1 1 1
全体の 合計 / 入庫数 15 25 14 54 54
全体の 合計 / 出庫数 12 15 15 42 42
全体の M在庫 3 13 12 12 12

4. 手順

1) 入庫表と出庫表の取得

  1. 在庫を算出するための在庫Excelブックを作成し、開きます。

  2. [データ]-[データの取得v]-[ファイルから>]-[Excelブックから]で入庫表のブックを選択し、[インポート]で入庫表のシートを選択し、[データの変換]でPower Queryエディタに取込みます。

  3. 右の名前を'入庫'に変更します。

  4. 右の"適用したステップ"の"ソース"と"ナビゲーション"の次のステップを右クリックし、[最後まで削除]。 ⇒ 自動生成された不要なステップが削除される。

  5. [Column2]列の▼で[null]のチェックを外し[OK]。 ⇒ [Column2]列の不要な行が削除される。

  6. [変換]-[1行目をヘッダとして使用v]-[1行目をヘッダとして使用]。 ⇒ 1行目がヘッダになる。

  7. 自動的に"変更された型"のステップが追加されるので、適切でない型アイコン(列名の左側)をクリックし、適切な型を選択し、[現在のものを置換]。 ⇒ データ型が設定される。

  8. [ホーム]-[閉じて読み込むv]-[閉じて次に読み込む...]で、"接続の作成のみ"と"このデータをデータモデルに追加する"を選び[OK]でExcelに戻ります。 ⇒ データモデルに追加される。

  9. 入庫表と同様の手順で出庫表を取得しデータモデルに追加します。クエリの名前は'出庫'にします。

参考 : クエリの名前がデータモデルのテーブル名になります。

2) 製品テーブルの作成

入庫テーブルと出庫を関連付ける製品テーブルを作成します。(これをディメンジョンテーブルと呼びます。)

  1. 右に"クエリと接続"が表示されていなければ、[データ]-[クエリと接続]をクリックして"クエリと接続"ペインを右に表示し、適当なクエリを右クリックして[編集]でPower Queryエディタを開きます。
  2. 左の'入庫'を右クリックし[参照]で新しいクエリを作成し、名前を'製品'に変更します。
  3. [品名]列を右クリックし、[その他の列の削除]。 ⇒ [品名]以外の列が削除される。
  4. [品名]列を右クリックし、[重複の削除]。 ⇒ 重複のない品名リストになる。
    参考 : ディメンジョンテーブルは、重複が無いリストにする必要があります。
  5. [ホーム]-[閉じて読み込むv]-[閉じて次に読み込む...]で、"接続の作成のみ"と"このデータをデータモデルに追加する"を選び[OK]でExcelに戻ります。 ⇒ データモデルに追加されます。

3) 日付テーブルの作成

Power Pivotでは、DAX式を簡単にするため、日付テーブルを使うことをお勧めします。最も分かりやすい作り方は、Excelのワークシートで作る方法[*1]ですが、ここでは入庫表と出庫表に連動した日付を生成するPower Query Mのコードで作成します。コードの意味はコメントを読んでください。M関数の仕様はGoogleで調べてください。理解できなければ、AI/LLMにこのコードの解説をしてもらってください。AI/LLMにこのコードを短くしてもらうこともできます。

  1. [データ]-[データの取得v]-[その他のデータソースから]-[空のクエリ]でPower Queryエディタに空のクエリを生成し、右の名前を'日付'に変更します。
  2. [ホーム]-[詳細エディタ]で次のコードを上書きします。
let
Source1 = 入庫, // [日付]列を持つクエリ名を設定
Source2 = 出庫, // [日付]列を持つクエリ名を設定

// ── 集計対象期間の決定 ──────────────────────────
  MinDate = Date.StartOfMonth(List.Min({List.Min(Source1[日付]), List.Min(Source2[日付])})),
MaxDate = Date.EndOfMonth(List.Max({List.Max(Source1[日付]), List.Max(Source2[日付])})),
// ── 日単位カレンダーの生成 ──────────────────────

DateList = List.Dates(MinDate, Duration.Days(MaxDate - MinDate) + 1, #duration(1, 0, 0, 0)),
CalendarTable = Table.FromList(DateList, Splitter.SplitByNothing(), {"日付"}),
型設定済 = Table.TransformColumnTypes(CalendarTable, {{"日付", type date}}),
// ── 会計年度(4月始まり)の基準日・年・月を1回だけ算出 ──

// EDATE([@日付], -3) 相当:3ヶ月前にずらすことで
// 会計年度の「年」と「月番号(4月=1~翌3月=12)」を取得
会計基準日追加 = Table.AddColumn(型設定済, "会計基準日", each Date.AddMonths([日付], -3), type date),
会計年追加 = Table.AddColumn(会計基準日追加, "会計年", each Date.Year([会計基準日]), Int64.Type),
会計月追加 = Table.AddColumn(会計年追加, "会計月", each Date.Month([会計基準日]), Int64.Type),
// ── 表示用列の追加 ──────────────────────────────


// 年:YEAR([@日付])

列_年 = Table.AddColumn(会計月追加, "年", each Date.Year([日付]), Int64.Type),

// 月:TEXT([@日付],"m月") ※ゼロ埋めなし

列_月 = Table.AddColumn(列_年, "月", each Text.From(Date.Month([日付])) & "月", type text),

// 年月:TEXT([@日付],"yy/mm") ※ゼロ埋めあり

列_年月 = Table.AddColumn(列_月, "年月", each
let
yy = Text.PadStart(Text.From(Date.Year([日付]) - 2000), 2, "0"),
mm = Text.PadStart(Text.From(Date.Month([日付])), 2, "0")
in
yy & "/" & mm,
type text),

// 四半期.:INT((MONTH(x)-1)/3)+1 & "Q" ※年度なし

列_四半期ドット = Table.AddColumn(列_年月, "四半期.", each
let
四半期番号 = Number.IntegerDivide([会計月] - 1, 3) + 1
in
Text.From(四半期番号) & "Q",
type text),
    // 四半期:(YEAR(x)-2000) & "/" & 四半期番号 & "Q" ※ゼロ埋めなし
列_四半期 = Table.AddColumn(列_四半期ドット, "四半期", each
let
四半期番号 = Number.IntegerDivide([会計月] - 1, 3) + 1
in
Text.From([会計年] - 2000) & "/" & Text.From(四半期番号) & "Q",
type text),

// 半期:(YEAR(x)-2000) & "/" & 半期番号 & "H" ※ゼロ埋めなし

列_半期 = Table.AddColumn(列_四半期, "半期", each
let
半期番号 = Number.IntegerDivide([会計月] - 1, 6) + 1
in
Text.From([会計年] - 2000) & "/" & Text.From(半期番号) & "H",
type text),

// 期:IF(MONTH(x)<7,"上期","下期") ※年度なし

列_期 = Table.AddColumn(列_半期, "期", each
if [会計月] < 7 then "上期" else "下期",
type text),

// 年度:(YEAR(x)-2000) & "年度" ※ゼロ埋めなし

列_年度 = Table.AddColumn(列_期, "年度", each
Text.From([会計年] - 2000) & "年度",
type text),

// 年度.:YEAR(x) ※西暦そのまま、年度なし表示との組み合わせ用

列_年度ドット = Table.AddColumn(列_年度, "年度.", each [会計年], Int64.Type),

// 内部計算用の一時列を削除し、ご指定の列順に並べ替え

不要列削除済 = Table.RemoveColumns(列_年度ドット, {"会計基準日", "会計年", "会計月"}),
最終結果 = Table.ReorderColumns(不要列削除済,
{"日付", "年月", "四半期", "半期", "年度", "年度.", "四半期.", "期", "月", "年"})
in
最終結果

補足 : このクエリは'入庫'クエリと'出庫'クエリの日付の範囲の日付テーブルを生成します。年度始まりを4月としています。最初のSource1とSource2にクエリ名を設定します。

  1. [ホーム]-[閉じて読み込むv]-[閉じて次に読み込む...]で、"接続の作成のみ"と"このデータをデータモデルに追加する"を選び[OK]でExcelに戻ります。 ⇒ データモデルに追加される。

参考 : 日付テーブルは、重複しない連続した日付を持つディメンジョンテーブルです。Power PivotやPower BIで日付テーブルを用いると、DAXのタイムインテリジェンス関数を使うことができます。

[*1] : Excelのワークシートで日付テーブルを作成する方法は、次の記事を見てください。

4) リレーションの作成

次のいずれかの方法でリレーションを設定します。

a) [Power Pivot]-[管理]-[表示]-[ダイアグラムビュー]。
b) [データ]-[データモデルv(緑アイコン)]-[データモデルの管理]-[ホーム]-[表示]-[ダイアグラムビュー]。
c) [データ]-[データモデルv(緑アイコン)]-[リレーションシップ]-[編集]。

リレーションの項目と方向('テーブル'[項目])

ファクトテーブル *--<--1 ディメンジョンテーブル(関連)
'入庫'[日付] *--<--1 '日付'[日付]
'出庫'[日付] *--<--1 '日付'[日付]
'入庫'[品名] *--<--1 '製品'[品名]
'出庫'[品名] *--<--1 '製品'[品名]

リレーションを設定する手順

ダイアグラムビューでは、*(多)から1へ項目をドラッグします。
リレーションシップでは、上段が*(多)で下段の関連が1になるようにテーブルと項目を設定します。

5) ピボットテーブルの生成

[挿入]-[ピボットテーブルv]-[データモデルから]でピボットテーブルを生成します。

6) メジャーの作成

  1. 次のいずれかの方法でメジャーを作成します。
    a) ① ピボットテーブルを選択し、右に"ピボットテーブルのフィールド"を表示。② メジャーを作成するテーブル('予算'または'実績)を右クリックし[メジャーの追加]。
    b) [Power Pivot]-[管理]でメジャーを書くテーブルの下の空白セルを選択。数式バーにメジャーを入力する。
    c) [データ]-[データモデル(緑アイコン)v]-[データモデルの管理]で、メジャーを書くテーブルの下の空白セルを選択。数式バーにメジャーを入力する。

入庫に作成するメジャー

M入庫累計:=CALCULATE(
SUM('入庫'[入庫数]),
FILTER(ALL('日付'), '日付'[日付] <= MAX('日付'[日付]))
)

解説 : ALL('日付')で全日付をFILTERの対象とし、コンテキストの日付(MAX('日付'[日付]))以下の日付にFILTERで絞り込み、SUM('入庫'[入庫数])で合計すると累計になります。MAX()の中はピボットテーブルのフィルタコンテキストが掛かっており、ピボットテーブルの日付を示します。例えば、1Q(第1四半期)であれば、MAX()は7月31日になるので、7月31日の累計が計算されます。

出庫に作成するメジャー

M出庫累計:=CALCULATE(
SUM('出庫'[出庫数]),
FILTER(ALL('日付'), '日付'[日付] <= MAX('日付'[日付]))
)

解説 : M入庫累計と同じ仕組みで累計を求めます。

M在庫:=[M入庫累計] - [M出庫累計]

解説 : 在庫 = 入庫累計 - 出庫累計

参考 : 3つのメジャーだけで、日、月、四半期、半期、年度の期末在庫を算出できます。このようにメジャーはワークシート式に比べて式の数が少なく、パラメータにセルアドレスを使用しないので、パラメータが何を指しているのかが分かりやすく、可読性が良いです。

7) ピボットテーブルの設定

  1. 次のように項目を設定します。
  • 列 : '日付'[年度]、'日付'[四半期.]、'日付'[月]
  • 行 : '製品'[品名]、Σ値
  • Σ値 : '入庫'[入庫数]、'出庫[出庫数]、[M在庫]
  1. [ピボットテーブル分析]-[オプション]-[レイアウトと書式]で"更新時に列幅を調整する"のチェックを外す。
  2. [ピボットテーブル分析]-[オプション]-[表示]で"データのないアイテムを行/列に表示する"のチェックを外す。(これをチェックする場合は、フィルターやスライサーで表示しないディメンジョン項目の値の選択を解除する。)
  3. [デザイン]-[レポートのレイアウト]-[表形式で表示]。
  4. [デザイン]-[総計v]-[列のみ集計を行う]。
  5. 小計を表示する列項目を右クリックし、["xxx"の小計]をクリックして✓マークを付ける。
  6. [ピボットテーブル分析]-[オプション]-[レイアウトと書式]でレイアウトの"セルとラベルを結合して中央揃えにする"をチェック。
  7. [デザイン]-[ピボットテーブルスタイル]でベースとするスタイルを右クリックし[複製]。ユーザー設定のスタイルを右クリックし[変更]でスタイルを変更。
  • 小計列 1 : 右罫線(太); 網掛け
  • 小計列 2 : 右罫線(細); 網掛け
  • 列小見出し 1 : 上罫線(太)
  • 列小見出し 2 : 上罫線(細)
  • 列小見出し 3 : 上罫線(細)

参考:ピボットテーブルの設定は、データモデルに影響しないので、ピボットテーブルをコピーして別の設定をすることが可能です。テーブルは、データモデルそのものなので、コピーして別の設定にすることができません。

9. サンプル

ダウンロード - 260805stock.zip

C:\Users\Public\Documents\260805stock に展開した各ファイルを置いてください。

10. 参考

| | コメント (0)

数量と単価の表が別で、単価表は改定日のみの場合のExcelによる売上金額計算

1. 前置き

数量と単価が1:1に対応しているデータでは、売上金額は「数量 × 単価」で求まります。
しかし、数量と単価が別の表で、単価表に改定日しかない場合は、数量表の日付が単価表に無い場合があるので、日付の完全一致条件では数量に対応した単価を求めることができません。また、日付だけでなく品名など単価を特定する条件が複数になると、VLOOKUPなどの関数の近似一致などの検索モードが使えなくなります。
なので、計算式に工夫が必要になります。

ピボットテーブルで集計するメジャーのDAX式でも同様の工夫が必要です。

DAX式は、式にセルアドレスでは無くテーブル名と列名を使用するので、変数の意味が分かりやすく、ワークシート式より可読性が良いです。

そこで、メジャーによる売上金額の集計式を紹介します。

2. 入力

本記事では、Excelのテーブルを入力として説明します。Power Queryで取込み・整形できるものなら、その他の形式の入力データでも良いです。

テーブル名 : '実績数'

日付 品名 数量
26/04/01 品A 1
26/04/01 品B 2
26/04/16 品A 1
26/05/01 品A 2
26/05/16 品B 2

テーブル名 : '実績単価' : 日付は単価適用開始日

日付 品名 単価
26/04/01 品A 10
26/04/01 品B 10
26/04/15 品A 11
26/05/15 品B 12

3. 出力

ピボットテーブル(Power Pivot、データモデルから)

年月 日付 品A 品B 全体の 合計 / 数量 全体の M売上 全体の M単価
合計 / 数量 M売上 M単価 合計 / 数量 M売上 M単価
26/04 26/04/01 1 10 10.0 2 20 10.0 3 30 10.0
26/04/16 1 11 11.0       1 11 11.0
26/04 集計 2 21 10.5 2 20 10.0 4 41 10.3
26/05 26/05/01 2 22 11.0       2 22 11.0
26/05/16       2 24 12.0 2 24 12.0
26/05 集計 2 22 11.0 2 24 12.0 4 46 11.5
総計 4 43 10.8 4 44 11.0 8 87 10.9

4. ワークシート式での集計(参考)

月次売上まで一気に計算することも可能だと思いますが、セルアドレスを変数とした複雑な式になり、可読性が悪化すると思われます。
そこで、'実績数'表に単価をXLOOKUPで参照して、参照値から売上金額を計算することで、各式を短くし可読性を上げます。また、単価を確認できるのでデバッグがしやすくなります。

◢ A B C D E F
1 実績売上 : ワークシート式      
2 年月 日付 品名 数量 単価 売上
3 26/04 26/04/01 品A 1 10 10
4 26/04 26/04/01 品B 2 10 20
5 26/04 26/04/16 品A 1 11 11
6 26/05 26/05/01 品A 2 11 22
7 26/05 26/05/16 品B 2 12 24
A3 = TEXT(B3, "yy/mm")
E3 = XLOOKUP(
B3,
FILTER(実績単価[日付], 実績単価[品名] = C3),
FILTER(実績単価[単価], 実績単価[品名] = C3),
"",
-1
)
F3 = D3 * E3

月次集計は、Excelの普通のピボットテーブルで集計します。

以下は、ワークシート関数による集計例です。

SUMIFSによる月次売上集計

◢ H I J
11 月次売上集計  
12 年月 品A 品B
13 26/04 21 20
14 26/05 22 24
H13 = UNIQUE(A3:A7)
I12 = TOROW(UNIQUE(C3:C7))
I13 = SUMIFS(F3:F7, A3:A7, H13#, C3:C7, I12#)

Microsoft 365であれば、PIVOTBYが使えるので、簡単になります。

PIVOTBYによる月次・日次売上集計

◢ L M N O P
17   品A 品B 合計  
18 26/04 26/04/01 10 20 30
19 26/04 26/04/16 11   11
20 26/04   21 20 41
21 26/05 26/05/01 22   22
22 26/05 26/05/16   24 24
23 26/05   22 24 46
24 総計   43 44 87
L17 = PIVOTBY(A3:B7, C3:C7, F3:F7, SUM,, 2)

余談 : 計算に関してはピボットテーブル程の自由度はありません。

5. Power Pivotによる集計(本編)

Power Pivotのメジャーを使うために、各テーブルをデータモデルに追加します。

1) '実績数'をデータモデルに追加

  1. '実績数'テーブルのセルを選択し、[データ]-[テーブルまたは範囲から]でPower Queryエディタに取込む。右の名前を"実績数'にする。
  2. 適切ではないデータ型の列を選択し、[変換]-[データ型]で適切な型に変更する。
  3. [ホーム]-[閉じて読み込むv]-[閉じて次に読み込む...]で、"接続の作成のみ"と"このデータをデータモデルに追加する"を選択し[OK]。

2) '実績単価'をデータモデルに追加

  1. '実績単価'テーブルのセルを選択し、[データ]-[テーブルまたは範囲から]でPower Queryエディタに取込む。右の名前を"実績単価'にする。
  2. 適切ではないデータ型の列を選択し、[変換]-[データ型]で適切な型に変更する。
  3. [ホーム]-[閉じて読み込むv]-[閉じて次に読み込む...]で、"接続の作成のみ"と"このデータをデータモデルに追加する"を選択し[OK]。

3) '日付'テーブルを生成しデータモデルに追加

本例では日付テーブルが無くても良いですが、前年比などを出す場合には日付テーブルが必要になります。(ここで示すPower Query Mのコードでは無く、Excelのワークシート式で作成してデータモデルに追加しても良いです。)

  1. [データ]-[データの追加v]-[他のデータソースから]-[空のクエリ]で、Power Queryエディタに空のクエリを生成する。右の名前を"日付"にする。
  2. [ホーム]-[詳細エディタ]で、次のコードに書き換える。
let
Source1 = 実績数,
Source2 = 実績単価,
    // ── 集計対象期間の決定 ──────────────────────────
  MinDate = Date.StartOfMonth(List.Min({List.Min(Source1[日付]), List.Min(Source2[日付])})),
MaxDate = Date.EndOfMonth(List.Max({List.Max(Source1[日付]), List.Max(Source2[日付])})),
    // ── 日単位カレンダーの生成 ──────────────────────
DateList = List.Dates(MinDate, Duration.Days(MaxDate - MinDate) + 1, #duration(1, 0, 0, 0)),
CalendarTable = Table.FromList(DateList, Splitter.SplitByNothing(), {"日付"}),
型設定済 = Table.TransformColumnTypes(CalendarTable, {{"日付", type date}}),
    // ── 会計年度(4月始まり)の基準日・年・月を1回だけ算出 ──
// EDATE([@日付], -3) 相当:3ヶ月前にずらすことで
// 会計年度の「年」と「月番号(4月=1~翌3月=12)」を取得
会計基準日追加 = Table.AddColumn(型設定済, "会計基準日", each Date.AddMonths([日付], -3), type date),
会計年追加 = Table.AddColumn(会計基準日追加, "会計年", each Date.Year([会計基準日]), Int64.Type),
会計月追加 = Table.AddColumn(会計年追加, "会計月", each Date.Month([会計基準日]), Int64.Type),
    // ── 表示用列の追加 ──────────────────────────────
    // 年:YEAR([@日付])
列_年 = Table.AddColumn(会計月追加, "年", each Date.Year([日付]), Int64.Type),
    // 月:TEXT([@日付],"m月") ※ゼロ埋めなし
列_月 = Table.AddColumn(列_年, "月", each Text.From(Date.Month([日付])) & "月", type text),
    // 年月:TEXT([@日付],"yy/mm") ※ゼロ埋めあり
列_年月 = Table.AddColumn(列_月, "年月", each
let
yy = Text.PadStart(Text.From(Date.Year([日付]) - 2000), 2, "0"),
mm = Text.PadStart(Text.From(Date.Month([日付])), 2, "0")
in
yy & "/" & mm,
type text),
    // 四半期.:INT((MONTH(x)-1)/3)+1 & "Q" ※年度なし
列_四半期ドット = Table.AddColumn(列_年月, "四半期.", each
let
四半期番号 = Number.IntegerDivide([会計月] - 1, 3) + 1
in
Text.From(四半期番号) & "Q",
type text),
    // 四半期:(YEAR(x)-2000) & "/" & 四半期番号 & "Q" ※ゼロ埋めなし
列_四半期 = Table.AddColumn(列_四半期ドット, "四半期", each
let
四半期番号 = Number.IntegerDivide([会計月] - 1, 3) + 1
in
Text.From([会計年] - 2000) & "/" & Text.From(四半期番号) & "Q",
type text),
    // 半期:(YEAR(x)-2000) & "/" & 半期番号 & "H" ※ゼロ埋めなし
列_半期 = Table.AddColumn(列_四半期, "半期", each
let
半期番号 = Number.IntegerDivide([会計月] - 1, 6) + 1
in
Text.From([会計年] - 2000) & "/" & Text.From(半期番号) & "H",
type text),
    // 期:IF(MONTH(x)<7,"上期","下期") ※年度なし
列_期 = Table.AddColumn(列_半期, "期", each
if [会計月] < 7 then "上期" else "下期",
type text),
    // 年度:(YEAR(x)-2000) & "年度" ※ゼロ埋めなし
列_年度 = Table.AddColumn(列_期, "年度", each
Text.From([会計年] - 2000) & "年度",
type text),
    // 年度.:YEAR(x) ※西暦そのまま、年度なし表示との組み合わせ用
列_年度ドット = Table.AddColumn(列_年度, "年度.", each [会計年], Int64.Type),
    // 内部計算用の一時列を削除し、ご指定の列順に並べ替え
不要列削除済 = Table.RemoveColumns(列_年度ドット, {"会計基準日", "会計年", "会計月"}),
最終結果 = Table.ReorderColumns(不要列削除済,
{"日付", "年月", "四半期", "半期", "年度", "年度.", "四半期.", "期", "月", "年"})
in
最終結果

解説 : '実績数'と'実績単価'テーブルの最小日の月初日から最大日の月末日の日付テーブルを生成します。4月始まりの年度列を生成します。これにより、月、四半期、半期、年度の集計が可能になります。また、DAXのタイムインテリジェンス関数が使えるようになります。

4) リレーションの作成

次のいずれかの方法でリレーションを設定する。

  1. [Power Pivot]-[管理]-[表示]-[ダイアグラムビュー]。
  2. [データ]-[データモデルv(緑アイコン)]-[データモデルの管理]-[ホーム]-[表示]-[ダイアグラムビュー]。
  3. [データ]-[データモデルv(緑アイコン)]-[リレーションシップ]-[編集]。

ファクトテーブル *--<--1 ディメンジョンテーブル(関連)
'実績数'[日付] *--<--1 '日付'[日付]

操作方法

  • ダイアグラムビューでは、(多)から1へ項目をドラッグする。
  • リレーションシップでは、上段が(多)で下段の関連が1になるようにテーブルと項目を設定する。

注意 : '実績単価'[日付]と'日付'[日付]は1:1に対応していないのでリレーションを取らない。(メジャーの式で双方の日付を比較処理します。)

5) ピボットテーブルの生成

[挿入]-[ピボットテーブルv]-[データモデルから]で、ピボットテーブルを生成する。

6) メジャーの作成

次のいずれかの方法でメジャーを作成する。

  1. ① ピボットテーブルを選択し、右に"ピボットテーブルのフィールド"を表示。② メジャーを作成するテーブル('予算'または'実績)を右クリックし[メジャーの追加]。式の先頭は「=」から始める。
  2. [Power Pivot]-[管理]でメジャーを書くテーブルの下の空白セルを選択。数式バーにメジャーを入力する。先頭は、「メジャー名:=」から始める。
  3. [データ]-[データモデル(緑アイコン)v]-[データモデルの管理]で、メジャーを書くテーブルの下の空白セルを選択。数式バーにメジャーを入力する。先頭は、「メジャー名:=」から始める。

'実績単価'に作成するメジャー

M直近単価:=CALCULATE(	// 第2引数で絞り込んだ範囲で第1引数の計算をする
MAX('実績単価'[単価]), // 第2引数のFILTERに合致する'実績単価'[単価])は1つ。MAXで取得
FILTER(
'実績単価', // '実績単価'テーブルを絞り込む
'実績単価'[品名] = MAX('実績数'[品名]) && // '実績単価'[品名]が'実績数'[品名]に等しい
'実績単価'[日付] <= MAX('実績数'[日付]) // '実績単価'[日付]が'実績数'[日付]以下
)
)

'実績数'に作成するメジャー
M売上:=SUMX(
'実績数',
'実績数'[数量] * [M直近単価]
)

解説 : SUMXは、第1引数の'実績数'テーブルの1行毎に、第2引数の掛け算を行ったものを合計します。

余談 : ワークシート式の売上は「I13 = SUMIFS(F3:F7, A3:A7, H13#, C3:C7, I12#)」ですが、この式のF3:F3だけでは何を合計しているのかが分からずそのアドレスの場所を見る必要があります。メジャーだと「'実績数'[数量] * [M直近単価]」なので、数量×単価を合計していることが一目で分かり、可読性が良いです。また、検索モードも、XLOOKUPの第5引数の「-1」より「CALCULATE(MAX(...FILTER(...'実績単価'[日付] <= MAX('実績数'[日付])」の方が何を計算しているのかが分かりやすいと思います。

M単価:=DIVIDE([M売上], SUM('実績数'[数量]))

解説:単価 = 売上金額 ÷ 数量。DIVIDEは、分母が0でもエラーを表示しない割り算関数。

余談 : 以上のように、メジャーは変数にセルアドレスでは無くテーブル名と列名を使用するので、変数が何を指しているのかが分かりやすく、可読性が良い。式の中にコメントが書けるので、可読性が良い。ワークシート関数に似ているDAX関数名があるので、分かりやすい。

7) ピボットテーブルの設定

  1. 次のように項目を設定します。
  • フィルター : '日付'[年度]。--> このようにした場合は、必ず1つの年度を選択する。
  • 列 : '実績数'[品名]、Σ値。
  • 行 : '日付'[年月]、'日付'[日付]。
  • Σ値 : '実績数'[数量](合計)、[M売上]、[M単価]。
  1. ピボットテーブルの[年度]の[▼]で、26年度以外のチェックを外す。

  2. [ピボットテーブル分析]-[オプション]-[レイアウトと書式]で、"更新時に列幅を調整する"のチェックを外す。

  3. [ピボットテーブル分析]-[オプション]-[表示]で、"データのないアイテムを行/列に表示する"のチェックを外す。(これをチェックする場合は、フィルターやスライサーで表示しないディメンジョン項目の選択を解除する。)

  4. [デザイン]-[レポートのレイアウト]-[表形式で表示]。

  5. [デザイン]-[総計v]-[行と列の集計を行う]。

  6. 小計を表示する列項目を右クリックし、["xxx"の小計]をクリックして✓マークを付ける。

  7. [デザイン]-[ピボットテーブルスタイル]でベースとするスタイルを右クリックし[複製]。ユーザー設定のスタイルを右クリックし[変更]でスタイルを変更。

  • 小計列 1 : 網掛け
  • 列小見出し 1 : 上罫線(太)
  • 列小見出し 2 : 上罫線(細)
  • 総計列 : 左罫線(太)、網掛け
  • 総計行 : 上罫線(太)、網掛け

参考 : ピボットテーブルは、セル毎の書式や同じ項目に異なる粒度の値を設定することができません。なので、ピボットテーブルではできない表は、集計をピボットテーブルで行い、最終的な表は普通のワークシート表からGETPIVOTDATAでピボットテーブルの値を参照することで作成します。

6. サンプル

ダウンロード - 260729lookup.xlsx

7. 関連情報

| | コメント (0)

Excel Power Pivotで利益偏差を単価要因と数量要因と原価要因に分ける

1. 前書き

予算と実績の売上偏差や利益偏差を売価要因と数量要因と原価要因に分ける計算を、Excel Power Pivotのメジャー(DAX)で行いました。
メジャーを使うと、ワークシート式より式の数が少なくなり、式にセルアドレスを使用しないので可読性が向上し、ミスが減ります。
本例では、Power Queryのヘルパークエリ[*1]を用いた複数シートの結合など、モダンExcelの色々な機能を使用していますが、モダンExcelを知らない人でも作成できるように手順を記載しています。
モダンExcelを知っている人は、途中を飛ばしてメジャーだけを見てください。
検証をあまりしていないので、使用される場合は検証を行い、自己責任で使用して下さい。

[*1] ヘルパークエリを使うと、Power Query Mのコードを直接書かずに、クリック操作で関数クエリを作成・編集することができます。

対予算売上偏差の数量要因と売価要因

売上偏差 = 実績数量 × 実績売価 - 予算数量 × 予算価
= 純粋な数量要因 + 純粋な売価要因 + 交差項(数量と売価が同時に動いた分)

「按分」なので、交差項を数量側・売価側に均等に振り分ける平均法(折半法)を使います。

数量要因 = (実績数量-予算数量) × (予算売価+実績売価)/2 ← 平均売価を使う
売価要因 = (実績売価-予算売価) × (予算数量+実績数量)/2 ← 平均数量を使う

とすると、数式的に必ず「数量要因 + 売価要因 = 売上偏差」が成立します(交差項がちょうど半分ずつ両方に含まれる形になるため)。

対予算利益偏差の数量要因と売価要因と原価要因

利益は 数量 × (売価 − 総原価) なので、変数を「数量」と「粗利売価(売価−原価)」の2つとみなすと、既存の M数量要因予 / M売価要因予 と全く同じ「平均法」の式が使えます。

粗利売価の差 Δ(売価−原価) をさらに「売価要因」と「原価要因」に分解すればよいので、3要因に拡張した形になります。

数量要因 = Δ数量 ×(実績粗利売価+予算粗利売価)/2
売価要因 = Δ売価 ×(実績数量+予算数量)/2
原価要因 = −Δ原価 ×(実績数量+予算数量)/2 ※原価上昇は利益を下げるのでマイナス

この3つを足すと [M利益差予] に一致します(売上偏差の2要因分解と同じ代数的恒等式を使っているため)。

💡 価偏差要因の調査方法

総原価の要因分析は、掘り下げが必要です。総原価の科目別などの内訳をモデル化すれば、掘り下げが可能になります。Excelのピボットテーブルをダブルクリックするとドリルダウンができまが、要因分析をするならPower BI他のBIツールの方が便利だと思います。例えば、Power BIの分解ツリーは、影響の大きいい要因をツリーを辿ることで調べることができます。

余談 : このビジュアルはExcelに欲しいですね。ExcelもGoogle Sheetsにシェアを奪われ始めているので、ExcelのBI機能を強化して欲しいです。Power BI for Excel Add Onがあると嬉しいです。個人作業の調査ツールとして使うのであれば、無料のPower BI Desktopで分解ツリーを使うことができます。Power Queryとメジャーは、Excelと全く同じものが使えます。

 

2. 入力

本記事の説明で取り扱うデータ構成のは一例であり、これ以外のファイル構成、シート構成、表の形でも、Power Queryで対応することができます。

1) 予算

26年度予算

項目 製品 26/04 26/05 途中省略 27/03
数量 製品A 11 11 16
製品B 6 6 8
売価 製品A 105 105 105
製品B 95 95 95
総原価 製品A 100 100 100
製品B 90 90 90

260721diff1

  • 予算ブックは1つで、各年度のシートがある。
  • 年月はyy/mm形式のテキスト型。

2) 実績

26年度実績

日付 製品 数量 売価 総原価
26/04/01 製品A 11 105 100
26/04/01 製品B 6 95 90
26/05/01 製品A 12 105 100
26/05/01 製品B 6 96 91
途中省略
27/03/01 製品A 15 105 100
27/03/01 製品B 8 94 89

260721diff2

  • 実績ブックは1つで、各年度のシートがある。
  • 日付はシリアル値。

3. 出力

1) 対予算偏差

値 製品 26年度 途中省略 26年度 集計
1Q 1Q 集計
4月 5月 6月
M売上 製品A 1,155 1,260 1,155 3,570 16,972
製品B 570 576 564 1,710 7,979
M利益 製品A 55 60 55 170 720
製品B 30 30 30 90 377
M利益差予 製品A ±0 +5 -5 ±0 -90
製品B ±0 ±0 ±0 ±0 -43
M利益差数量要因予 製品A ±0 +5 -5 ±0 ±0
製品B ±0 ±0 ±0 ±0 ±0
M利益差売価要因予 製品A ±0 ±0 ±0 ±0 -38
製品B ±0 +6 -6 ±0 -1
M利益差原価要因予 製品A ±0 ±0 ±0 ±0 -52
製品B ±0 -6 +6 ±0 -42
全体の M売上 1,725 1,836 1,719 5,280 24,951
全体の M利益 85 90 85 260 1,097
全体の M利益差予 ±0 +5 -5 ±0 -133
全体の M利益差数量要因予 ±0 +5 -5 ±0 ±0
全体の M利益差売価要因予 ±0 +6 -6 ±0 -39
全体の M利益差原価要因予 ±0 -6 +6 ±0 -94

260721diff3

2) 対前年偏差

値 製品 26年度 途中省略 26年度 集計
1Q 1Q 集計
4月 5月 6月
M売上 製品A 1,155 1,260 1,155 3,570 16,972
製品B 570 576 564 1,710 7,979
M売上前年 製品A 990 1,089 1,287 3,366 15,828
製品B 623 534 445 1,602 7,522
M売上偏差前年 製品A +165 +171 -132 +204 +1,144
製品B -53 +42 +119 +108 +457
M数量要因前年 製品A +102 +102 -204 ±0 +306
製品B -92 ±0 +92 ±0 ±0
M売価要因前年 製品A +63 +69 +72 +204 +838
製品B +39 +42 +28 +108 +457
全体の M売上 1,725 1,836 1,719 5,280 24,951
全体の M売上前年 1,613 1,623 1,732 4,968 23,350
全体の M売上偏差前年 +112 +213 -13 +312 +1,601
全体の M数量要因前年 +10 +102 -113 ±0 +306
全体の M売価要因前年 +102 +111 +100 +312 +1,295

260721diff4

4. データモデルの作成

1) 予算データの取込みと整形

複数シートのクロス表をデータベース形式に整形し、縦結合してデータモデルに追加します。

データの取得

  1. [データ]-[データの取得v]-[ファイルから>]-[Excelブックから]で予算ブックを選択し[インポート]、整形のサンプルとするシートを選択し[データの変換]でPower Queryエディタに取込み、右のクエリの名前を"サンプルシート予"に変更。
  2. 右の"ソース"と"ナビゲーション"以外のステップの左の✘をクリックして削除。ソースとナビゲーションの2ステップだけにする。 ⇒ サンプルシート予が完成。

パラメータクエリの作成

  1. [ホーム]-[新しいソースv]-[その他のソース>]-[空のクエリ]で右の名前を"パラメータ予"に変更。
  2. 左の"パラメータ1"を右クリックし、[パラメータへ変換する]。
  3. [ホーム]-[詳細エディタ]で次のように修正し[完了]。 ⇒ ヘルパークエリの"パラメータ予"が完成。
変更前
"" meta [IsParameterQuery=true, Type="any", IsParameterQueryRequired=true]
変更後
#"サンプルシート予" meta [IsParameterQuery=true, BinaryIdentifier=#"サンプルシート予", Type="Table", IsParameterQueryRequired=true]

サンプルシートの変換クエリの作成

  1. [ホーム]-[新しいソースv]-[その他のソース>]-[空のクエリ]で新しいクエリを作成し、右の名前を"サンプルシート予の変換"に変更します。
  2. 数式バーに次の様に入力し、✓をクリックする。 ⇒ サンプルシートが表示される。
= パラメータ予

右の"ソース"以外の左の✘をクリックして削除。ソースだけにする。
  1. [ホーム]-[行の削除v]-[上位の行の削除]で行数に1を入れ[OK]。 ⇒ 不要な行が削除される。
  2. "Coloumn1"列を選択し[変換]-[フィルv]-[下へ]。>項目列のnullが埋まる。
  3. [変換]-[1行目をヘッダーとして使用v]-[1行目をヘッダーとして使用]。 ⇒ 項目名がヘッダーになる。
  4. "項目"と"製品"列をCtrlキーを押しながら選択し、[変換]-[列のピボット解除v]-[その他の列のピボット解除]。 ⇒ 年月が1列に並ぶ。
  5. 変更された型が生成されたら、それを削除。ピボット解除された他の列の数式バーの"属性"を"年月"に書き換える。
  6. "項目"列を選択し[変換]-[列のピボット]で値列に[値]、詳細設定オプションに[集計しない]で[OK]。 ⇒ "数量"列と"売価"列ができる。
  7. [列の追加]-[カスタム列]で次のように入力し[OK]。 ⇒ テキスト形式の"年月"列から日付形式の"日付"列が生成される。
新しい列名:日付
カスタム列の式:
= #date(
Number.FromText("20"&Text.BeforeDelimiter([年月],"/")),
Number.FromText(Text.AfterDelimiter([年月],"/")),
1)

Ctrl+aで全列を選択し、[変換]-[データ型の検出]。正しく型設定されない場合は、個別に列を選択して[変換]-[データ型]で適切な型に変更。 ⇒ 型が設定される。

関数クエリの作成

  1. 左の"サンプル予シートの変換"クエリを右クリックし[関数の作成...]。
    関数名に"シート予の変換"と入力し[OK]。

注意:関数クエリのコードを直接編集しないでください。編集する場合は、サンプルシートの変換クエリを編集します。すると、関数クエリが連動して修正されます。これがヘルパークエリの機能です。

結合クエリの作成

  1. 左の"サンプル予シート"クエリを右クリックし[複製]で新しいクエリを作成し、右の名前を"予算"に変更する。
  2. 右の"ナビゲーション"のステップを削除し、ソースだけにする。 ⇒ これでブック全体が取り込まれた状態になる。
  3. "Kind"列の▼-[テキストフィルター>]-[指定の値に等しい...]で値にSheetを選択し[OK]。 ⇒ 縦結合するシートのみが残る。
  4. [列の追加]-[カスタム関数の呼び出し]で関数クエリに[シート予の変換]、パラメータ1に[Date]、新しい列名に"シート予の変換"を入力し[OK]。 ⇒ 各シートを関数クエリで変換したTableが格納された列が追加される。
  5. "シート予の変換"列を右クリックし[他の列の削除]。 ⇒ 不要な列が削除される。
  6. "Table"列の右の展開アイコンをクリックし、さらに読み込むをクリックし、"プレフィックスを追加する"のチェックを外し[OK]。 ⇒ 各シートの変換後の表を展開して縦結合される。
  7. [変換]-[データ型の検出]。正しく型設定されない場合は、個別に列を選択して[変換]-[データ型]で適切な型に変更。 ⇒ 型が設定される。
  8. 左の"サンプル予シート"を"シート予の変換"グループに移動。
  9. [ホーム]-[閉じて読み込むv]-[閉じて次に読み込む]で"接続の作成のみ"を選択し[OK]。 ⇒ Excelに戻る。
  10. "クエリと接続"の'予算'クエリを右クリックし、[読み込み先]で"接続の作成のみ"と"このモデルをデータモデルに追加する"を選択し[OK]。 ⇒ '予算'データモデルが完成。

2) 実績データの取込みと整形

複数シートのデータベース表を縦結合して、データモデルに追加します。

データの取得

  1. [データ]-[データの取得v]-[ファイルから>]-[Excelブックから]で実績ブックを選択し[インポート]、整形のサンプルとするシートを選択し[データの変換]でPower Queryエディタに取込み、右のクエリの名前を"サンプルシート実"に変更。
  2. 右の"ソース"と"ナビゲーション"以外のステップの左の✘をクリックして削除。ソースとナビゲーションの2ステップだけにする。 ⇒ サンプルシート実が完成。

パラメータクエリの作成

  1. [ホーム]-[新しいソースv]-[その他のソース>]-[空のクエリ]で右の名前を"パラメータ実"に変更。
  2. 左の"パラメータ1"を右クリックし、[パラメータへ変換する]。
  3. [ホーム]-[詳細エディタ]で次のように修正し[完了]。 ⇒ ヘルパークエリの"パラメータ実"が完成。
変更前
"" meta [IsParameterQuery=true, Type="any", IsParameterQueryRequired=true]
変更後
#"サンプルシート実" meta [IsParameterQuery=true, BinaryIdentifier=#"サンプルシート実", Type="Table", IsParameterQueryRequired=true]

サンプルシートの変換クエリの作成
  1. [ホーム]-[新しいソースv]-[その他のソース>]-[空のクエリ]で新しいクエリを作成し、右の名前を"サンプルシート実の変換"に変更します。
  2. 数式バーに次の様に入力し、✓をクリックする。 ⇒ サンプルシートが表示される。
= パラメータ実

右の"ソース"以外の左の×をクリックして削除。ソースだけにする。
  1. [ホーム]-[行の削除v]-[上位の行の削除]で行数に1を入れ[OK]。 ⇒ 不要な行が削除される。
  2. [変換]-[1行目をヘッダーとして使用v]-[1行目をヘッダーとして使用]。 ⇒ 項目名がヘッダーになる。
  3. 正しい型になっていない列を選択して[変換]-[データ型]で適切な型に変更。 ⇒ 型が設定される。

関数クエリの作成

  1. 左の"サンプル実シートの変換"クエリを右クリックし[関数の作成...]。
    関数名に"シート実の変換"と入力し[OK]。

結合クエリの作成

  1. 左の"サンプル実シート"クエリを右クリックし[複製]で新しいクエリを作成し、右の名前を"実績"に変更するす。
  2. 右の"ナビゲーション"のステップを削除し、ソースだけにする。 ⇒ これでブック全体が取り込まれた状態になる。
  3. "Kind"列の▼-[テキストフィルター>]-[指定の値に等しい...]で値にSheetを選択し[OK]。 ⇒ 縦結合するシートのみが残る。
  4. [列の追加]-[カスタム関数の呼び出し]で、関数クエリに[シート実の変換]、パラメータ1に[Date]、新しい列名に"シート実の変換"を入力し[OK]。 ⇒ 各シートを関数クエリで変換したTableが格納された列が追加される。
  5. "シート実の変換"列を右クリックし[他の列の削除]。 ⇒ 不要な列が削除される。
  6. "Table"列の右の展開アイコンをクリックし、さらに読み込むをクリックし、"プレフィックスを追加する"のチェックを外し[OK]。 ⇒ 各シートの変換後の表を展開して縦結合される。
  7. [変換]-[データ型の検出]。正しく型設定されない場合は、個別に列を選択して[変換]-[データ型]で適切な型に変更。 ⇒ 型が設定される。
  8. 左の"サンプル実シート"を"シート実の変換"グループに移動。
  9. [ホーム]-[閉じて読み込むv]-[閉じて次に読み込む]で、"接続の作成のみ"を選択し[OK]。 ⇒ Excelに戻る。
  10. "クエリと接続"の'実績'クエリを右クリックし、[読み込み先]で"接続の作成のみ"と"このモデルをデータモデルに追加する"を選択し[OK]。 ⇒ '実績'データモデルが完成。

3) 日付テーブルの生成

  1. [データ]-[データの取得v]-[その他のデータソースから]-[空のクエリ]でPower Queryエディタに"クエリ1"を生成し、クエリの名前を"日付"に変更する。
  2. [ホーム]-[詳細エディタ]で、既存のコードを次のコードで上書きする。
let
Source = 実績,
    // ── 集計対象期間の決定 ──────────────────────────
MinDate = Date.StartOfMonth(List.Min(Source[日付])),
MaxDate = Date.EndOfMonth(List.Max(Source[日付])),
    // ── 日単位カレンダーの生成 ──────────────────────
DateList = List.Dates(MinDate, Duration.Days(MaxDate - MinDate) + 1, #duration(1, 0, 0, 0)),
CalendarTable = Table.FromList(DateList, Splitter.SplitByNothing(), {"日付"}),
型設定済 = Table.TransformColumnTypes(CalendarTable, {{"日付", type date}}),
    // ── 会計年度(4月始まり)の基準日・年・月を1回だけ算出 ──
// EDATE([@日付], -3) 相当:3ヶ月前にずらすことで
// 会計年度の「年」と「月番号(4月=1~翌3月=12)」を取得
会計基準日追加 = Table.AddColumn(型設定済, "会計基準日", each Date.AddMonths([日付], -3), type date),
会計年追加 = Table.AddColumn(会計基準日追加, "会計年", each Date.Year([会計基準日]), Int64.Type),
会計月追加 = Table.AddColumn(会計年追加, "会計月", each Date.Month([会計基準日]), Int64.Type),
    // ── 表示用列の追加 ──────────────────────────────
    // 年:YEAR([@日付])
列_年 = Table.AddColumn(会計月追加, "年", each Date.Year([日付]), Int64.Type),
    // 月:TEXT([@日付],"m月") ※ゼロ埋めなし
列_月 = Table.AddColumn(列_年, "月", each Text.From(Date.Month([日付])) & "月", type text),
    // 年月:TEXT([@日付],"yy/mm") ※ゼロ埋めあり
列_年月 = Table.AddColumn(列_月, "年月", each
let
yy = Text.PadStart(Text.From(Date.Year([日付]) - 2000), 2, "0"),
mm = Text.PadStart(Text.From(Date.Month([日付])), 2, "0")
in
yy & "/" & mm,
type text),
    // 四半期.:INT((MONTH(x)-1)/3)+1 & "Q" ※年度なし
列_四半期ドット = Table.AddColumn(列_年月, "四半期.", each
let
四半期番号 = Number.IntegerDivide([会計月] - 1, 3) + 1
in
Text.From(四半期番号) & "Q",
type text),
    // 四半期:(YEAR(x)-2000) & "/" & 四半期番号 & "Q" ※ゼロ埋めなし
列_四半期 = Table.AddColumn(列_四半期ドット, "四半期", each
let
四半期番号 = Number.IntegerDivide([会計月] - 1, 3) + 1
in
Text.From([会計年] - 2000) & "/" & Text.From(四半期番号) & "Q",
type text),
    // 半期:(YEAR(x)-2000) & "/" & 半期番号 & "H" ※ゼロ埋めなし
列_半期 = Table.AddColumn(列_四半期, "半期", each
let
半期番号 = Number.IntegerDivide([会計月] - 1, 6) + 1
in
Text.From([会計年] - 2000) & "/" & Text.From(半期番号) & "H",
type text),
    // 期:IF(MONTH(x)<7,"上期","下期") ※年度なし
列_期 = Table.AddColumn(列_半期, "期", each
if [会計月] < 7 then "上期" else "下期",
type text),
    // 年度:(YEAR(x)-2000) & "年度" ※ゼロ埋めなし
列_年度 = Table.AddColumn(列_期, "年度", each
Text.From([会計年] - 2000) & "年度",
type text),
    // 年度.:YEAR(x) ※西暦そのまま、年度なし表示との組み合わせ用
列_年度ドット = Table.AddColumn(列_年度, "年度.", each [会計年], Int64.Type),
    // 内部計算用の一時列を削除し、ご指定の列順に並べ替え
不要列削除済 = Table.RemoveColumns(列_年度ドット, {"会計基準日", "会計年", "会計月"}),
最終結果 = Table.ReorderColumns(不要列削除済,
{"日付", "年月", "四半期", "半期", "年度", "年度.", "四半期.", "期", "月", "年"})
in
最終結果
  1. [ホーム]-[閉じて読み込むv]-[閉じて次に読み込む]で、"接続の作成のみ"と"このモデルをデータモデルに追加する"を選択し[OK]。 ⇒ '日付'データモデルが完成。

参考:日付テーブルをPower Query Mのコードで生成する代わりに、Excelのワークシートで作ることもできます。この場合、ワークシートに作る日付の範囲は、予算と実績ファイルの全ての日付を含むようにしてください。

4) 製品テーブルの生成

  1. [データ]-[クエリと接続]で右に"クエリと接続"を表示し、'予算'クエリを右クリックして[編集]でPower Queryエディタを開く。
  2. '予算'クエリを右クリックし[参照]で新しいクエリを作成し、クエリの名前を'製品'に変更する。
  3. [ホーム]-[クエリの追加v]-[クエリの追加]で[実績]を選択し[OK]。
  4. [品名]列を右クリックし[他の列の削除]。
  5. [品名]列を右クリックし[重複の削除]。
  6. [ホーム]-[閉じて読み込むv]-[閉じて次に読み込む]で、"接続の作成のみ"と"このモデルをデータモデルに追加する"を選択し[OK]。 ⇒ '製品'データモデルが完成。

5. リレーションの作成

  1. 次のいずれかの方法でリレーションを設定する。
    a) [Power Pivot]-[管理]-[表示]-[ダイアグラムビュー]。
    b) [データ]-[データモデルv(緑アイコン)]-[データモデルの管理]-[ホーム]-[表示]-[ダイアグラムビュー]。
    c) [データ]-[データモデルv(緑アイコン)]-[リレーションシップ]-[編集]。

ファクトテーブル *--<--1 ディメンジョンテーブル(関連)
'予算'[日付] *--<--1 '日付'[日付]
'実績'[日付] *--<--1 '日付'[日付]
'予算'[製品] *--<--1 '製品'[製品]
'実績'[製品] *--<--1 '製品'[製品]

ダイアグラムビューでは、*(多)から1へ項目をドラッグする。
リレーションシップでは、上段が*(多)で下段の関連が1になるように項目を設定する。

6. ピボットテーブルの生成

  1. [挿入]-[ピボットテーブルv]-[データモデルから]で、ピボットテーブルを生成する。

7. メジャーの作成

  1. 次のいずれかの方法でメジャーを作成する。
    a) ① ピボットテーブルを選択し、右に"ピボットテーブルのフィールド"を表示。② メジャーを作成するテーブル('予算'または'実績)を右クリックし[メジャーの追加]。式の先頭は「=」から始める。
    b) [Power Pivot]-[管理]でメジャーを書くテーブルの下の空白セルを選択。数式バーにメジャーを入力する。先頭は、「メジャー名:=」から始める。
    c) [データ]-[データモデル(緑アイコン)v]-[データモデルの管理]で、メジャーを書くテーブルの下の空白セルを選択。数式バーにメジャーを入力する。先頭は、「メジャー名:=」から始める。

予算に作成するメジャー

M予売上:=SUMX('予算', '予算'[数量] * '予算'[売価])

コンテキストの'予算'テーブルの1行毎に「'予算'[数量] * '予算'[売価]」を計算して合計する。
M予数量:=SUM('予算'[数量])
M予売価:=DIVIDE([M予売上], [M予数量])

[M予売上]÷[M予数量]の意味ですが、[M予数量]が0の時はエラーを返しません。

 

M予原価:=DIVIDE(SUMX('予算', '予算'[数量] * '予算'[総原価]), [M予数量])
M予利益:=SUMX('予算', ('予算'[売価] - '予算'[総原価]) * '予算'[数量])
M予利益率:=DIVIDE([M予利益], [M予売上])
M利益差予:=[M利益] - [M予利益]
M利益率差予:=[M利益率] - [M予利益率]
M利益差数量要因予:=SUMX(
VALUES('製品'[製品]), // CROSSJOIN(VALUES('製品'[製品]), VALUES('顧客'[顧客]))
([M数量] - [M予数量])
* (([M売価] - [M原価]) + ([M予売価] - [M予原価])) / 2
)


対予算利益差数量要因 = 数量差 × 平均利益 です。利益 = 売価 - 原価 です。利益の実績と予算の平均を用います。

 

M利益差売価要因予:=SUMX(
VALUES('製品'[製品]), // CROSSJOIN(VALUES('製品'[製品]), VALUES('顧客'[顧客]))
([M売価] - [M予売価]) * ([M数量] + [M予数量]) / 2
)


対予算利益差売価要因 = 売価差 × 平均数量 です。数量に実績と予算の平均数量を用います。これは[M売価要因予]と同じです。

 

M利益差原価要因予:=SUMX(
VALUES('製品'[製品]), // CROSSJOIN(VALUES('製品'[製品]), VALUES('顧客'[顧客]))
-([M原価] - [M予原価]) * ([M数量] + [M予数量]) / 2
)


対予算利益差原価要因 = 原価差 × 平均数量 です。数量に実績と予算の平均数量を用います。

 

M売上偏差予:=[M売上] - [M予売上]

M数量要因予:=SUMX(
VALUES('製品'[製品]), // CROSSJOIN(VALUES('製品'[製品]), VALUES('顧客'[顧客])),
([M数量] - [M予数量]) * ([M売価] + [M予売価]) / 2
)


売上数量偏差 = 数量差 × 売価 です。売価に実績と予算の平均売価を用います。[*1]

 

M売価要因予:=SUMX(
VALUES('製品'[製品]), // CROSSJOIN(VALUES('製品'[製品]), VALUES('顧客'[顧客])),
([M売価] - [M予売価]) * ([M数量] + [M予数量]) / 2
)


売上売価偏差 = 売価差 × 数量 です。数量に当年と前年の平均数量を用います。これは[M利益差売価要因予]と同じです。[*1]

 

実績に作成するメジャー

M売上:=SUMX('実績', '実績'[数量] * '実績'[売価])

コンテキストの'実績'テーブルの1行毎に「'実績'[数量] * '実績'[売価]」を計算して合計する。
M数量:=SUM('実績'[数量])

M売価:=DIVIDE([M売上], [M数量])

[M売上]÷[M数量]の意味ですが、[M数量]が0の時はエラーを返しません。

 

M原価:=DIVIDE(SUMX('実績', '実績'[数量] * '実績'[総原価]), [M数量])
M利益:=SUMX('実績', ('実績'[売価] - '実績'[総原価]) * '実績'[M数量])

利益 = (売価 - 原価) × 数量 です。

 

M利益率:=DIVIDE([M利益], [M売上])
M売上前年:=CALCULATE([M売上], SAMEPERIODLASTYEAR('日付'[日付]))

CALCULATEで前年同期を示すSAMEPERIODLASTYEARにコンテキストを変更して[M売上]の中で合計SUMを求める。
M数量前年:=CALCULATE([M数量], SAMEPERIODLASTYEAR('日付'[日付]))

CALCULATEで前年同期を示すSAMEPERIODLASTYEARにコンテキストを変更して[M数量]の中で合計SUMを求める。
M売価前年:=DIVIDE([M売上前年], [M数量前年])

M売上偏差前年:=[M売上] - [M売上前年]

M数量要因前年:=SUMX(
VALUES('製品'[製品]), // CROSSJOIN(VALUES('製品'[製品]), VALUES('顧客'[顧客])),
([M数量] - [M数量前年]) * ([M売価] + [M売価前年]) / 2
)


売上数量偏差 = 数量差 × 売価 です。売価に当年と前年の平均売価を用います。[*1]

 

M売価要因前年:=SUMX(
VALUES('製品'[製品]), // CROSSJOIN(VALUES('製品'[製品]), VALUES('顧客'[顧客])),
([M売価] - [M売価前年]) * ([M数量] + [M数量前年]) / 2
)


売上売価偏差 = 売価差 × 数量 です。数量に当年と前年の平均数量を用います。[*1]

 

[*1] : この計算は1グループ毎に行う必要があります。なので、複数の分類を含むグループの場合は、CROSSJOINを用いてグループのリストを作成してください。例えば、顧客と製品でグループ化する場合は、CROSSJOIN(VALUES('製品'[製品]), VALUES('顧客'[顧客])) とします。

8. ピボットテーブルの設定

1) 対予偏差

  1. 次のように項目を設定します。
  • 列 : '日付'[年度]、'日付'[四半期.]、'日付'[月]
  • 行 : Σ値、'製品'[製品]
  • Σ値 : [M売上]、[M利益]、[M利益差予]、[M利益差数量要因予]、[M利益差売価要因予]、[M利益差原価要因予]
  1. ピボットテーブルの[年度]の[▼]で26年度以外のチェックを外す。
  2. [ピボットテーブル分析]-[オプション]-[レイアウトと書式]で"更新時に列幅を調整する"のチェックを外す。
  3. [ピボットテーブル分析]-[オプション]-[表示]で"データのないアイテムを行/列に表示する"のチェックを外す。(これをチェックする場合は、フィルターやスライサーで表示しないディメンジョン項目の選択を解除する。)
  4. [デザイン]-[レポートのレイアウト]-[表形式で表示]。
  5. [デザイン]-[総計v]-[列のみ集計を行う]。
  6. 小計を表示する列項目を右クリックし、["xxx"の小計]をクリックして✓マークを付ける。
  7. [デザイン]-[ピボットテーブルスタイル]でベースとするスタイルを右クリックし[複製]。ユーザー設定のスタイルを右クリックし[変更]でスタイルを変更。
  • 小計列 1 : 右罫線(太); 網掛け
  • 小計列 2 : 右罫線(細); 網掛け
  • 列小見出し 1 : 上罫線(太)
  • 列小見出し 2 : 上罫線(細)

2) 対前年偏差

  1. 対予偏差のピボットテーブルをコピーします。
  2. 次のように項目を設定します。
  • 列 : '日付'[年度]、'日付'[四半期.]、'日付'[月]
  • 行 : Σ値、'製品'[製品]
  • Σ値 : [M売上]、[M売上前年]、[M売上偏差前年]、[M数量要因前年]、[M売価要因前年]

参考:ピボットテーブルの設定は、データモデルに影響しないので、ピボットテーブルをコピーして別の設定をすることが可能です。一方テーブルは、データモデルそのものなので、コピーして別の設定にすることができません。

9. サンプル

ダウンロード - 260721diff.zip

C:\Users\Public\Documents\260721diff に展開した各ファイルを置いてください。

10. 参考

| | コメント (0)

Excel Power Pivotで売上偏差を単価要因と数量要因に分ける

1. 前書き

予算と実績や前年と当年の売上偏差を単価要因と数量要因に分ける計算を、Excel Power Pivotのメジャー(DAX)で行いました。
メジャーを使うと、ワークシート式より式の数が少なくなり、式にセルアドレスを使用しないので可読性が向上し、ミスが減ります。
本例では、Power Queryのヘルパークエリ[*1]を用いた複数シートの結合など、モダンExcelの色々な機能を使用していますが、Power Pivotを知らない人でも作成できるように、その手順も記載しています。
Power Pivotを知っている人は、途中を飛ばしてメジャーだけを見てください。
検証をあまりしていないので、使用される場合は検証を行い、自己責任で使用して下さい。

[*1] ヘルパークエリを使うと、Power Query Mのコードを直接書かずに、クリック操作で関数クエリを作成・編集することができます。

計算式

前年(添字0)と当年(添字1)で

数量: Q₀, Q₁
売価: P₀, P₁
売上: Q × P

とすると

売上偏差 = Q₁P₁ - Q₀P₀
= 純粋な数量要因 + 純粋な売価要因 + 交差項(数量と売価が同時に動いた分)

「按分」なので、交差項を数量側・売価側に均等に振り分ける**平均法(折半法)**が最もシンプルかつ実務でよく使われます。これは

数量要因 = (Q₁-Q₀) × (P₀+P₁)/2 ← 平均売価を使う
売価要因 = (P₁-P₀) × (Q₀+Q₁)/2 ← 平均数量を使う

とすると、数式的に必ず「数量要因 + 売価要因 = 売上偏差」が成立します(交差項がちょうど半分ずつ両方に含まれる形になるため)。

2. 入力

本記事の説明で取り扱うデータの構成は一例であり、これ以外のファイル構成、シート構成、表の形でも、Power Queryで対応することができます。

1) 予算

26年度予算

項目 商品 26/04 26/05 途中省略 27/03
数量 商品A 11 11 16
商品B 6 6 8
単価 商品A 105 105 105
商品B 95 95 95

260718diff1

  • 予算ブックは1つで、各年度のシートがある。
  • 年月はyy/mm形式のテキスト型。

2) 実績

26年度実績

日付 商品 数量 単価
26/04/01 商品A 11 105
26/04/01 商品B 6 95
26/05/01 商品A 12 105
26/05/01 商品B 6 96
途中省略
27/03/01 商品A 15 105
27/03/01 商品B 8 94

260718diff2

  • 実績ブックは1つで、各年度のシートがある。
  • 日付はシリアル値。

3. 出力

1) 対予算偏差

値 商品 26年度 途中省略 26年度 集計
1Q 1Q 集計
4月 5月 6月
M売上 商品A 1,155 1,260 1,155 3,570 16,972
商品B 570 576 564 1,710 7,979
M予売上 商品A 1,155 1,155 1,260 3,570 17,010
商品B 570 570 570 1,710 7,980
M売上偏差予 商品A ±0 +105 -105 ±0 -38
商品B ±0 +6 -6 ±0 -1
M数量要因予 商品A ±0 +105 -105 ±0 ±0
商品B ±0 ±0 ±0 ±0 ±0
M単価要因予 商品A ±0 ±0 ±0 ±0 -38
商品B ±0 +6 -6 ±0 -1
全体の M売上 1,725 1,836 1,719 5,280 24,951
全体の M予売上 1,725 1,725 1,830 5,280 24,990
全体の M売上偏差予 ±0 +111 -111 ±0 -39
全体の M数量要因予 ±0 +105 -105 ±0 ±0
全体の M単価要因予 ±0 +6 -6 ±0 -39

260718diff3

 

2) 対前年偏差

値 商品 26年度 途中省略 26年度 集計
1Q 1Q 集計
4月 5月 6月
M売上 商品A 1,155 1,260 1,155 3,570 16,972
商品B 570 576 564 1,710 7,979
M売上前年 商品A 990 1,089 1,287 3,366 15,828
商品B 623 534 445 1,602 7,522
M売上偏差前年 商品A +165 +171 -132 +204 +1,144
商品B -53 +42 +119 +108 +457
M数量要因前年 商品A +102 +102 -204 ±0 +306
商品B -92 ±0 +92 ±0 ±0
M単価要因前年 商品A +63 +69 +72 +204 +838
商品B +39 +42 +28 +108 +457
全体の M売上 1,725 1,836 1,719 5,280 24,951
全体の M売上前年 1,613 1,623 1,732 4,968 23,350
全体の M売上偏差前年 +112 +213 -13 +312 +1,601
全体の M数量要因前年 +10 +102 -113 ±0 +306
全体の M単価要因前年 +102 +111 +100 +312 +1,295

260718diff4

4. データモデルの作成

1) 予算データの取込みと整形

複数シートのクロス表をデータベース形式に整形し、縦結合してデータモデルに追加します。

データの取得

  1. [データ]-[データの取得v]-[ファイルから>]-[Excelブックから]で予算ブックを選択し[インポート]、整形のサンプルとするシートを選択し[データの変換]でPower Queryエディタに取込み、右のクエリの名前を"サンプルシート予"に変更。
  2. 右の"ソース"と"ナビゲーション"以外のステップの左の✘をクリックして削除。ソースとナビゲーションの2ステップだけにする。 ⇒ サンプルシート予が完成。

パラメータクエリの作成

  1. [ホーム]-[新しいソースv]-[その他のソース>]-[空のクエリ]で右の名前を"パラメータ予"に変更。
  2. 左の"パラメータ1"を右クリックし、[パラメータへ変換する]。
  3. [ホーム]-[詳細エディタ]で次のように修正し[完了]。 ⇒ ヘルパークエリの"パラメータ予"が完成。
変更前
"" meta [IsParameterQuery=true, Type="any", IsParameterQueryRequired=true]
変更後
#"サンプルシート予" meta [IsParameterQuery=true, BinaryIdentifier=#"サンプルシート予", Type="Table", IsParameterQueryRequired=true]

サンプルシートの変換クエリの作成

  1. [ホーム]-[新しいソースv]-[その他のソース>]-[空のクエリ]で新しいクエリを作成し、右の名前を"サンプルシート予の変換"に変更します。
  2. 数式バーに次の様に入力し、✓をクリックする。 ⇒ サンプルシートが表示される。
= パラメータ予

右の"ソース"以外の左の✘をクリックして削除。ソースだけにする。
  1. [ホーム]-[行の削除v]-[上位の行の削除]で行数に1を入れ[OK]。 ⇒ 不要な行が削除される。
  2. "Coloumn1"列を選択し[変換]-[フィルv]-[下へ]。>項目列のnullが埋まる。
  3. [変換]-[1行目をヘッダーとして使用v]-[1行目をヘッダーとして使用]。 ⇒ 項目名がヘッダーになる。
  4. "項目"と"商品"列をCtrlキーを押しながら選択し、[変換]-[列のピボット解除v]-[その他の列のピボット解除]。 ⇒ 年月が1列に並ぶ。
  5. 変更された型が生成されたら、それを削除。ピボット解除された他の列の数式バーの"属性"を"年月"に書き換える。
  6. "項目"列を選択し[変換]-[列のピボット]で値列に[値]、詳細設定オプションに[集計しない]で[OK]。 ⇒ "数量"列と"単価"列ができる。
  7. [列の追加]-[カスタム列]で次のように入力し[OK]。 ⇒ テキスト形式の"年月"列から日付形式の"日付"列が生成される。
新しい列名:日付
カスタム列の式:
= #date(
Number.FromText("20"&Text.BeforeDelimiter([年月],"/")),
Number.FromText(Text.AfterDelimiter([年月],"/")),
1)

Ctrl+aで全列を選択し、[変換]-[データ型の検出]。正しく型設定されない場合は、個別に列を選択して[変換]-[データ型]で適切な型に変更。 ⇒ 型が設定される。

関数クエリの作成

  1. 左の"サンプル予シートの変換"クエリを右クリックし[関数の作成...]。
    関数名に"シート予の変換"と入力し[OK]。

注意:関数クエリのコードを直接編集しないでください。編集する場合は、サンプルシートの変換クエリを編集します。すると、関数クエリが連動して修正されます。これがヘルパークエリの機能です。

結合クエリの作成

  1. 左の"サンプル予シート"クエリを右クリックし[複製]で新しいクエリを作成し、右の名前を"予算"に変更する。
  2. 右の"ナビゲーション"のステップを削除し、ソースだけにする。 ⇒ これでブック全体が取り込まれた状態になる。
  3. "Kind"列の▼-[テキストフィルター>]-[指定の値に等しい...]で値にSheetを選択し[OK]。 ⇒ 縦結合するシートのみが残る。
  4. [列の追加]-[カスタム関数の呼び出し]で関数クエリに[シート予の変換]、パラメータ1に[Date]、新しい列名に"シート予の変換"を入力し[OK]。 ⇒ 各シートを関数クエリで変換したTableが格納された列が追加される。
  5. "シート予の変換"列を右クリックし[他の列の削除]。 ⇒ 不要な列が削除される。
  6. "Table"列の右の展開アイコンをクリックし、さらに読み込むをクリックし、"プレフィックスを追加する"のチェックを外し[OK]。 ⇒ 各シートの変換後の表を展開して縦結合される。
  7. [変換]-[データ型の検出]。正しく型設定されない場合は、個別に列を選択して[変換]-[データ型]で適切な型に変更。 ⇒ 型が設定される。
  8. 左の"サンプル予シート"を"シート予の変換"グループに移動。
  9. [ホーム]-[閉じて読み込むv]-[閉じて次に読み込む]で"接続の作成のみ"を選択し[OK]。 ⇒ Excelに戻る。
  10. "クエリと接続"の'予算'クエリを右クリックし、[読み込み先]で"接続の作成のみ"と"このモデルをデータモデルに追加する"を選択し[OK]。 ⇒ '予算'データモデルが完成。

2) 実績データの取込みと整形

複数シートのデータベース表を縦結合して、データモデルに追加します。

データの取得

  1. [データ]-[データの取得v]-[ファイルから>]-[Excelブックから]で実績ブックを選択し[インポート]、整形のサンプルとするシートを選択し[データの変換]でPower Queryエディタに取込み、右のクエリの名前を"サンプルシート実"に変更。
  2. 右の"ソース"と"ナビゲーション"以外のステップの左の✘をクリックして削除。ソースとナビゲーションの2ステップだけにする。 ⇒ サンプルシート実が完成。

パラメータクエリの作成

  1. [ホーム]-[新しいソースv]-[その他のソース>]-[空のクエリ]で右の名前を"パラメータ実"に変更。
  2. 左の"パラメータ1"を右クリックし、[パラメータへ変換する]。
  3. [ホーム]-[詳細エディタ]で次のように修正し[完了]。 ⇒ ヘルパークエリの"パラメータ実"が完成。
変更前
"" meta [IsParameterQuery=true, Type="any", IsParameterQueryRequired=true]
変更後
#"サンプルシート実" meta [IsParameterQuery=true, BinaryIdentifier=#"サンプルシート実", Type="Table", IsParameterQueryRequired=true]

サンプルシートの変換クエリの作成
  1. [ホーム]-[新しいソースv]-[その他のソース>]-[空のクエリ]で新しいクエリを作成し、右の名前を"サンプルシート実の変換"に変更します。
  2. 数式バーに次の様に入力し、✓をクリックする。 ⇒ サンプルシートが表示される。
= パラメータ実

右の"ソース"以外の左の×をクリックして削除。ソースだけにする。
  1. [ホーム]-[行の削除v]-[上位の行の削除]で行数に1を入れ[OK]。 ⇒ 不要な行が削除される。
  2. [変換]-[1行目をヘッダーとして使用v]-[1行目をヘッダーとして使用]。 ⇒ 項目名がヘッダーになる。
  3. 正しい型になっていない列を選択して[変換]-[データ型]で適切な型に変更。 ⇒ 型が設定される。

関数クエリの作成

  1. 左の"サンプル実シートの変換"クエリを右クリックし[関数の作成...]。
    関数名に"シート実の変換"と入力し[OK]。

結合クエリの作成

  1. 左の"サンプル実シート"クエリを右クリックし[複製]で新しいクエリを作成し、右の名前を"実績"に変更するす。
  2. 右の"ナビゲーション"のステップを削除し、ソースだけにする。 ⇒ これでブック全体が取り込まれた状態になる。
  3. "Kind"列の▼-[テキストフィルター>]-[指定の値に等しい...]で値にSheetを選択し[OK]。 ⇒ 縦結合するシートのみが残る。
  4. [列の追加]-[カスタム関数の呼び出し]で、関数クエリに[シート実の変換]、パラメータ1に[Date]、新しい列名に"シート実の変換"を入力し[OK]。 ⇒ 各シートを関数クエリで変換したTableが格納された列が追加される。
  5. "シート実の変換"列を右クリックし[他の列の削除]。 ⇒ 不要な列が削除される。
  6. "Table"列の右の展開アイコンをクリックし、さらに読み込むをクリックし、"プレフィックスを追加する"のチェックを外し[OK]。 ⇒ 各シートの変換後の表を展開して縦結合される。
  7. [変換]-[データ型の検出]。正しく型設定されない場合は、個別に列を選択して[変換]-[データ型]で適切な型に変更。 ⇒ 型が設定される。
  8. 左の"サンプル実シート"を"シート実の変換"グループに移動。
  9. [ホーム]-[閉じて読み込むv]-[閉じて次に読み込む]で、"接続の作成のみ"を選択し[OK]。 ⇒ Excelに戻る。
  10. "クエリと接続"の'実績'クエリを右クリックし、[読み込み先]で"接続の作成のみ"と"このモデルをデータモデルに追加する"を選択し[OK]。 ⇒ '実績'データモデルが完成。

3) 日付テーブルの生成

  1. [データ]-[データの取得v]-[その他のデータソースから]-[空のクエリ]でPower Queryエディタに"クエリ1"を生成し、クエリの名前を"日付"に変更する。
  2. [ホーム]-[詳細エディタ]で、既存のコードを次のコードで上書きする。
let
Source = 実績,
    // ── 集計対象期間の決定 ──────────────────────────
MinDate = Date.StartOfMonth(List.Min(Source[日付])),
MaxDate = Date.EndOfMonth(List.Max(Source[日付])),
    // ── 日単位カレンダーの生成 ──────────────────────
DateList = List.Dates(MinDate, Duration.Days(MaxDate - MinDate) + 1, #duration(1, 0, 0, 0)),
CalendarTable = Table.FromList(DateList, Splitter.SplitByNothing(), {"日付"}),
型設定済 = Table.TransformColumnTypes(CalendarTable, {{"日付", type date}}),
    // ── 会計年度(4月始まり)の基準日・年・月を1回だけ算出 ──
// EDATE([@日付], -3) 相当:3ヶ月前にずらすことで
// 会計年度の「年」と「月番号(4月=1~翌3月=12)」を取得
会計基準日追加 = Table.AddColumn(型設定済, "会計基準日", each Date.AddMonths([日付], -3), type date),
会計年追加 = Table.AddColumn(会計基準日追加, "会計年", each Date.Year([会計基準日]), Int64.Type),
会計月追加 = Table.AddColumn(会計年追加, "会計月", each Date.Month([会計基準日]), Int64.Type),
    // ── 表示用列の追加 ──────────────────────────────
    // 年:YEAR([@日付])
列_年 = Table.AddColumn(会計月追加, "年", each Date.Year([日付]), Int64.Type),
    // 月:TEXT([@日付],"m月") ※ゼロ埋めなし
列_月 = Table.AddColumn(列_年, "月", each Text.From(Date.Month([日付])) & "月", type text),
    // 年月:TEXT([@日付],"yy/mm") ※ゼロ埋めあり
列_年月 = Table.AddColumn(列_月, "年月", each
let
yy = Text.PadStart(Text.From(Date.Year([日付]) - 2000), 2, "0"),
mm = Text.PadStart(Text.From(Date.Month([日付])), 2, "0")
in
yy & "/" & mm,
type text),
    // 四半期.:INT((MONTH(x)-1)/3)+1 & "Q" ※年度なし
列_四半期ドット = Table.AddColumn(列_年月, "四半期.", each
let
四半期番号 = Number.IntegerDivide([会計月] - 1, 3) + 1
in
Text.From(四半期番号) & "Q",
type text),
    // 四半期:(YEAR(x)-2000) & "/" & 四半期番号 & "Q" ※ゼロ埋めなし
列_四半期 = Table.AddColumn(列_四半期ドット, "四半期", each
let
四半期番号 = Number.IntegerDivide([会計月] - 1, 3) + 1
in
Text.From([会計年] - 2000) & "/" & Text.From(四半期番号) & "Q",
type text),
    // 半期:(YEAR(x)-2000) & "/" & 半期番号 & "H" ※ゼロ埋めなし
列_半期 = Table.AddColumn(列_四半期, "半期", each
let
半期番号 = Number.IntegerDivide([会計月] - 1, 6) + 1
in
Text.From([会計年] - 2000) & "/" & Text.From(半期番号) & "H",
type text),
    // 期:IF(MONTH(x)<7,"上期","下期") ※年度なし
列_期 = Table.AddColumn(列_半期, "期", each
if [会計月] < 7 then "上期" else "下期",
type text),
    // 年度:(YEAR(x)-2000) & "年度" ※ゼロ埋めなし
列_年度 = Table.AddColumn(列_期, "年度", each
Text.From([会計年] - 2000) & "年度",
type text),
    // 年度.:YEAR(x) ※西暦そのまま、年度なし表示との組み合わせ用
列_年度ドット = Table.AddColumn(列_年度, "年度.", each [会計年], Int64.Type),
    // 内部計算用の一時列を削除し、ご指定の列順に並べ替え
不要列削除済 = Table.RemoveColumns(列_年度ドット, {"会計基準日", "会計年", "会計月"}),
最終結果 = Table.ReorderColumns(不要列削除済,
{"日付", "年月", "四半期", "半期", "年度", "年度.", "四半期.", "期", "月", "年"})
in
最終結果
  1. [ホーム]-[閉じて読み込むv]-[閉じて次に読み込む]で、"接続の作成のみ"と"このモデルをデータモデルに追加する"を選択し[OK]。 ⇒ '日付'データモデルが完成。

参考:日付テーブルをPower Query Mのコードで生成する代わりに、Excelのワークシートで作ることもできます。この場合、ワークシートに作る日付の範囲は、予算と実績ファイルの全ての日付を含むようにしてください。

4) 商品テーブルの生成

  1. [データ]-[クエリと接続]で右に"クエリと接続"を表示し、'予算'クエリを右クリックして[編集]でPower Queryエディタを開く。
  2. '予算'クエリを右クリックし[参照]で新しいクエリを作成し、クエリの名前を'商品'に変更する。
  3. [ホーム]-[クエリの追加v]-[クエリの追加]で[実績]を選択し[OK]。
  4. [品名]列を右クリックし[他の列の削除]。
  5. [品名]列を右クリックし[重複の削除]。
  6. [ホーム]-[閉じて読み込むv]-[閉じて次に読み込む]で、"接続の作成のみ"と"このモデルをデータモデルに追加する"を選択し[OK]。 ⇒ '商品'データモデルが完成。

5. リレーションの作成

  1. 次のいずれかの方法でリレーションを設定する。
    a) [Power Pivot]-[管理]-[表示]-[ダイアグラムビュー]。
    b) [データ]-[データモデルv(緑アイコン)]-[データモデルの管理]-[ホーム]-[表示]-[ダイアグラムビュー]。
    c) [データ]-[データモデルv(緑アイコン)]-[リレーションシップ]-[編集]。

ファクトテーブル *--<--1 ディメンジョンテーブル(関連)
'予算'[日付] *--<--1 '日付'[日付]
'実績'[日付] *--<--1 '日付'[日付]
'予算'[商品] *--<--1 '商品'[商品]
'実績'[商品] *--<--1 '商品'[商品]

ダイアグラムビューでは、*(多)から1へ項目をドラッグする。
リレーションシップでは、上段が*(多)で下段の関連が1になるように項目を設定する。

6. ピボットテーブルの生成

  1. [挿入]-[ピボットテーブルv]-[データモデルから]で、ピボットテーブルを生成する。

7. メジャーの作成

  1. 次のいずれかの方法でメジャーを作成する。
    a) ① ピボットテーブルを選択し、右に"ピボットテーブルのフィールド"を表示。② メジャーを作成するテーブル('予算'または'実績)を右クリックし[メジャーの追加]。式の先頭は「=」から始める。
    b) [Power Pivot]-[管理]でメジャーを書くテーブルの下の空白セルを選択。数式バーにメジャーを入力する。先頭は、「メジャー名:=」から始める。
    c) [データ]-[データモデル(緑アイコン)v]-[データモデルの管理]で、メジャーを書くテーブルの下の空白セルを選択。数式バーにメジャーを入力する。先頭は、「メジャー名:=」から始める。

予算に作成するメジャー

M予売上:=SUMX('予算', '予算'[数量] * '予算'[単価])

コンテキストの'予算'テーブルの1行毎に「'予算'[数量] * '予算'[単価]」を計算して合計する。
M予数量:=SUM('予算'[数量])
M予単価:=DIVIDE([M予売上], [M予数量])

[M予売上]÷[M予数量]の意味ですが、[M予数量]が0の時はエラーを返しません。
M売上偏差予:=[M売上] - [M予売上]

M数量要因予:=SUMX(
VALUES('商品'[商品]), // CROSSJOIN(VALUES('商品'[商品]), VALUES('顧客'[顧客])),
([M数量] - [M予数量]) * ([M単価] + [M予単価]) / 2
)

売上数量偏差 = 数量差 × 単価 です。単価に実績と予算の平均単価を用います。[*1]
M単価要因予:=SUMX(
VALUES('商品'[商品]), // CROSSJOIN(VALUES('商品'[商品]), VALUES('顧客'[顧客])),
([M単価] - [M予単価]) * ([M数量] + [M予数量]) / 2
)

売上単価偏差 = 単価差 × 数量 です。数量に当年と前年の平均数量を用います。[*1]

実績に作成するメジャー

M売上:=SUMX('実績', '実績'[数量] * '実績'[単価])

コンテキストの'実績'テーブルの1行毎に「'実績'[数量] * '実績'[単価]」を計算して合計する。
M数量:=SUM('実績'[数量])

M単価:=DIVIDE([M売上], [M数量])

[M売上]÷[M数量]の意味ですが、[M数量]が0の時はエラーを返しません。
M売上前年:=CALCULATE([M売上], SAMEPERIODLASTYEAR('日付'[日付]))

CALCULATEで前年同期を示すSAMEPERIODLASTYEARにコンテキストを変更して[M売上]の中で合計SUMを求める。
M数量前年:=CALCULATE([M数量], SAMEPERIODLASTYEAR('日付'[日付]))

CALCULATEで前年同期を示すSAMEPERIODLASTYEARにコンテキストを変更して[M数量]の中で合計SUMを求める。
M単価前年:=DIVIDE([M売上前年], [M数量前年])

M売上偏差前年:=[M売上] - [M売上前年]

M数量要因前年:=SUMX(
VALUES('商品'[商品]), // CROSSJOIN(VALUES('商品'[商品]), VALUES('顧客'[顧客])),
([M数量] - [M数量前年]) * ([M単価] + [M単価前年]) / 2
)

売上数量偏差 = 数量差 × 単価 です。単価に当年と前年の平均単価を用います。[*1]
M単価要因前年:=SUMX(
VALUES('商品'[商品]), // CROSSJOIN(VALUES('商品'[商品]), VALUES('顧客'[顧客])),
([M単価] - [M単価前年]) * ([M数量] + [M数量前年]) / 2
)

売上単価偏差 = 単価差 × 数量 です。数量に当年と前年の平均数量を用います。[*1]

[*1] : この計算は1グループ毎に行う必要があります。なので、複数の分類を含むグループの場合は、CROSSJOINを用いてグループのリストを作成してください。例えば、顧客と商品でグループ化する場合は、CROSSJOIN(VALUES('商品'[商品]), VALUES('顧客'[顧客])) とします。

8. ピボットテーブルの設定

1) 対予偏差

  1. 次のように項目を設定します。
  • 列 : '日付'[年度]、'日付'[四半期.]、'日付'[月]
  • 行 : Σ値、'商品'[商品]
  • Σ値 : [M売上]、[M売上予]、[M売上偏差予]、[M数量要因予]、[M単価要因予]
  1. ピボットテーブルの[年度]の[▼]で26年度以外のチェックを外す。
  2. [ピボットテーブル分析]-[オプション]-[レイアウトと書式]で"更新時に列幅を調整する"のチェックを外す。
  3. [ピボットテーブル分析]-[オプション]-[表示]で"データのないアイテムを行/列に表示する"のチェックを外す。(これをチェックする場合は、フィルターやスライサーで表示しないディメンジョン項目の選択を解除する。)
  4. [デザイン]-[レポートのレイアウト]-[表形式で表示]。
  5. [デザイン]-[総計v]-[列のみ集計を行う]。
  6. 小計を表示する列項目を右クリックし、["xxx"の小計]をクリックして✓マークを付ける。
  7. [デザイン]-[ピボットテーブルスタイル]でベースとするスタイルを右クリックし[複製]。ユーザー設定のスタイルを右クリックし[変更]でスタイルを変更。
  • 小計列 1 : 右罫線(太); 網掛け
  • 小計列 2 : 右罫線(細); 網掛け
  • 列小見出し 1 : 上罫線(太)
  • 列小見出し 2 : 上罫線(細)

2) 対前年偏差

  1. 対予偏差のピボットテーブルをコピーします。
  2. 次のように項目を設定します。
  • 列 : '日付'[年度]、'日付'[四半期.]、'日付'[月]
  • 行 : Σ値、'商品'[商品]
  • Σ値 : [M売上]、[M売上前年]、[M売上偏差前年]、[M数量要因前年]、[M単価要因前年]

参考:ピボットテーブルの設定は、データモデルに影響しないので、ピボットテーブルをコピーして別の設定をすることが可能です。テーブルは、データモデルそのものなので、コピーして別の設定にすることができません。

9. サンプル

ダウンロード - 260718diff.zip

C:\Users\Public\Documents\260718diff に展開した各ファイルを置いてください。

10. 参考

| | コメント (0)

入庫単価が変動する物の在庫金額と回転率 Excel Power Pivot

1. 前置き

Yahoo!知恵袋に、入庫単価が変動する物の在庫管理の質問がありました。先入先出(FIFO:First-In First-Out)が前提です。
https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q12328004942

在庫数は、「入庫累計 - 出庫累計」や「前日の在庫 + 当日の入庫 - 当日の出庫」で求めます。
一方、出庫金額や在庫金額は、複数の入庫単価が含まれているケースを考慮する必要があり、式を考えるのが面倒です。
そこで、Claudeを利用してExcelのメジャー(DAX式)を作りました。更に金額ベースの回転率や在庫日数を求めるメジャーもClaudeを利用して作りました。出庫金額は、原価計算などにも利用できると思われます。

生産管理や調達業務の経験が無いので、もっと良い方法があるのかも知れません。
メジャーが長いので、計算に時間が掛かるかも知れません。遅いようであれば、ピボットテーブルに表示する範囲を絞ってください。例えば、日毎のピボットテーブルは1ヶ月のみ表示、月毎のピボットテーブルは日付を項目に設定しないなどです。ピボットテーブルは、コピーして異なる表示にすることができます。

(トレーサビリティが可能なシステムであれば、ロット番号と単価の紐づけにより単価が特定できすので、本記事より処理が簡単になります。)

検証をあまりしていないので、使用される場合は以下を読んで式を理解し、検証をした上で自己責任でお願いいたします。間違いがあれば、コメントをください。

参考:メジャーとは

Power Pivotはセルアドレスを使用しないので、ある程度セルアドレス間違いを軽減することができます。

2. 入力

テーブル名:入庫

日付 品名 入庫単価 入庫数
26/04/01 品A 1 50
26/04/01 品B 10 5
26/04/15 品A 2 10
26/05/01 品B 20 5
26/05/05 品A 3 50

260515zaikokin1 

実務では入庫表と単価表が分離していて、単価表は適用開始日のみの場合もあると思います。その場合は、Power Queryで入庫表に入庫単価をマージ、ソート、フィルするか、メジャーで単価表から単価を拾うようにすればよいです。前者の方が簡単ですが、後者の方がファイルが小さくなります。後者のDAX式もそれほど難しくないです。「6. 単価表が独立しているケース」を見てください。

テーブル名:出庫

日付 品名 出庫数
26/04/05 品A 40
26/04/05 品B 2
26/05/10 品B 6
26/05/20 品A 40

260515zaikokin2 

テーブル名:カレンダ

Power Pivotで時間軸の集計を行うので、日付テーブルを使用します。これは他の集計でも共通で使用できるので、共有フォルダに入れておくと良いです。これにより、四半期集計、前年同期差、等も出せるようになります。
【注意】必ず入庫・出庫の全日付を含むようにしてください。入庫・出庫の全日付を含む日付テーブルを自動生成する場合は、下記の参考を参照してください。

日付 年月 四半期 半期 年度 年度. 四半期. 期 月 年
26/04/01 26/04 26/1Q 26/1H 26年度 2026 1Q 上期 4月 2026
26/04/02 26/04 26/1Q 26/1H 26年度 2026 1Q 上期 4月 2026
省略                  
28/03/31 28/03 27/4Q 27/2H 27年度 2027 4Q 下期 3月 2027

260515zaikokin3 

  1. Excelで項目行と空の1行の表を作成し、その範囲を全選択し[挿入]-[テーブル]でテーブル化し、[テーブルデザイン]でテーブル名を`カレンダ'に変更します。
  2. 日付セルをA2とした場合の数式を次に示します。この式を必要な日付範囲まで下にコピーします。
A3 = 最初の日付(シリアル値)
A4 = A3 + 1
B3 = TEXT([@日付], "yy/mm")
C3 = LET(x, EDATE([@日付], -3), (YEAR(x)-2000) & "/" & (INT((MONTH(x) -1) / 3) + 1) & "Q")
D3 = LET(x, EDATE([@日付], -3), (YEAR(x) - 2000) & "/" & (INT((MONTH(x) - 1) / 6) + 1) & "H")
E3 = YEAR(EDATE([@日付], -3)) - 2000 & "年度"
F3 = YEAR(EDATE([@日付], -3))
G3 = INT((MONTH(EDATE([@日付], -3)) - 1) / 3) + 1 & "Q"
H3 = IF(MONTH(EDATE([@日付], -3)) < 7, "上期", "下期")
I3 = TEXT([@日付], "m月")
J3 = YEAR([@日付])

参考

自動的にPower Queryで、元データの範囲の日付テーブルを生成することもできます。自動的に生成すると、元データの日付が日付テーブルの範囲を超えて、集計漏れが発生することを防止することができます。次の記事に、日付テーブルのPower Query Mのコードがあります。

3. 出力

ピボットテーブルを出力します。

  品A
年月 日付 M入庫単価 合計 / 入庫数 合計 / 出庫数 M在庫数 M入庫金額 M出庫金額 M在庫金額 M回転率A
26/04 26/04/01 ¥1.0 50   50 ¥50   ¥50  
26/04/05     40 10   ¥40 ¥10  
26/04/15 ¥2.0 10   20 ¥20   ¥30  
26/04 集計 ¥1.2 60 40 20 ¥70 ¥40 ¥30 1.5
26/05 26/05/01       20     ¥30  
26/05/05 ¥3.0 50   70 ¥150   ¥180  
26/05/10       70     ¥180  
26/05/20     40 30   ¥90 ¥90  
26/05 集計 ¥3.0 50 40 30 ¥150 ¥90 ¥90 0.7

260515zaikokin4

4. 作成手順概要

1) Excelの表をデータモデルに追加

  1. 入庫表を全選択し、[挿入]-[テーブル]でテーブル化し、[テーブルデザイン]で名前を"入庫"に変更する。
    (テーブル化は必須では無いですが、後の処理を簡略化するために、本例ではテーブルにしています。)
  2. テーブルを選択し、[データ]-[テーブルまたは範囲から]で、Power Queryエディタに取込む。
  3. "日付"の左側の型アイコンをクリックし、[日付]。 ⇒ 型が日付になる。
  4. [ホーム]-[閉じて読み込むv]-[閉じて次に読み込む]で、"接続の作成のみ"と"このモデルをデータモデルに追加する"を選択し[OK]。

同様にして'出庫'、'カレンダ'をデータモデルに追加します。

2) '品目'テーブルの作成

  1. [データ]-[クエリと接続]で右のクエリの接続に表示されるクエリを右クリックし、[編集]。 ⇒ Power Queryエディタが開く。
  2. 左の'入庫'クエリを右クリックし、[参照]、右のプロパティの名前を"品名"に変更。 ⇒ "品名"クエリができる。
  3. "品名"列を右クリックし、[他の列の削除]。 ⇒ "品名"列のみになる。
  4. "品名"列を右クリックし、[重複の削除]。⇒ 品名の重複が無くなる。(ディメンジョンテーブルは、重複があるとリレーションできない。)
  5. [ホーム]-[閉じて読み込むv]-[閉じて次に読み込む]で、"接続の作成のみ"と"このモデルをデータモデルに追加する"を選択し[OK]。

260515zaikokin5 

3) リレーション

[データ]-[データモデル(緑アイコン)v]-[データモデルの管理]-[ホーム]-[ダイアグラムビュー]で、次のように項目をドラッグし、リレーションを作成します。ドラッグする方向は、ファクトテーブル(*)からディメンジョンテーブル(1)です。

'入庫'[日付] *:1 'カレンダ'[日付]
'出庫'[日付] *:1 'カレンダ'[日付]
'入庫'[品名] *:1 '品名'[品名]
'出庫'[品名] *:1 '品名'[品名]

260515zaikokin6 

4) ピボットテーブルの作成

  1. [挿入]-[ピボットテーブルv]-[データモデルから]。⇒ ピボットテーブルが作られます。
  2. [ピボットテーブル分析]-[ピボットテーブルv]-[オプション]-[レイアウトと書式]で、"更新時に列幅を自動調整する"のチェックを外す。
  3. [デザイン]-[レポートのレイアウト]-[表形式で表示]。

7) メジャー(DAX式)

ピボットテーブルのフィールドの'注文'または'完成'を右クリックし、[メジャーの追加]で次のメジャーを作成します。
以下、:=の左がメジャーの名前で、:=の右以降がメジャーの式です。

260515zaikokin7 

参考 : 別のメジャーの編集画面

Power Pivot for Excelの数式バーでも、メジャーを編集することができます。数式バーは、編集領域の拡大ができ、コードの要素別に色分けされるので、長い式を編集しやすいです。

操作手順:[Power Pivot]-[管理]-[ホーム]-[データビュー]の画面で編集します。下部のタブでメジャーを書くデータモデルを選択し、タブの上のセルでメジャーを書くセルを選択し、数式バーにメジャー名と式を入力します。先頭は、「メジャー名:=」で書き始めます。

メジャーは、どのデータモデルに書いても同じ動作をします。書くデータモデルは、ピボットテーブルのフィールドの表示場所を決めるだけのものです。
メジャーを書いたセルの移動はできません。

260515zaikokin8 

参考

「5. サンプル」には、本記事の式とは別に、作業用の中間メジャーを作成して分割して式を短くした「260515zaikoR2.xlsx」もあります。

'入庫'に作成するメジャー

M入庫累計

'入庫'または'出庫'データがある日の入庫数の累計です。データが無い日はBLANKです。ピボットテーブルで表示しないか、フィルターやスライサーで表示日の範囲を絞るなら、IFを削除し累計式だけでOKです。

M入庫累計:=IF(
CALCULATE(COUNTROWS('入庫'), ALL('品名'[品名])) > 0
|| CALCULATE(COUNTROWS('出庫'), ALL('品名'[品名])) > 0,
CALCULATE(
SUM('入庫'[入庫数]),
FILTER(
ALL('カレンダ'),
'カレンダ'[日付] <= MAX('カレンダ'[日付])
)
)
)

参考:メジャーで累計を求める仕組みは次の記事を見てください。

M入庫単価

入庫単価です。直接入庫単価を表示するのではなく、「単価 = 金額 / 数量」で逆算することにより、合計の平均単価を表示できるようにします。

M入庫単価:=DIVIDE([M入庫金額], SUM('入庫'[入庫数]))

M入庫金額

入庫金額 = 単価 * 数量 です。合計の場合は、複数のレコードを含むので、イテレータ関数SUMXで行毎に金額を計算したものを合計します。

M入庫金額:=SUMX('入庫', '入庫'[入庫単価] * '入庫'[入庫数])

参考

イテレータ関数とは、第1引数のテーブルに対し1行ずつ第2引数以降の処理をする関数です。

'出庫'に作成するメジャー

M出庫累計

'入庫'または'出庫'データがある日の出庫数の累計です。データが無い日はBLANKです。ピボットテーブルで表示しないか、フィルターやスライサーで表示日の範囲を絞るなら、IFを削除し累計式だけでOKです。

M出庫累計:=IF(
CALCULATE(COUNTROWS('入庫'), ALL('品名'[品名])) > 0
|| CALCULATE(COUNTROWS('出庫'), ALL('品名'[品名])) > 0,
CALCULATE(
SUM('出庫'[出庫数]),
FILTER(
ALL('カレンダ'),
'カレンダ'[日付] <= MAX('カレンダ'[日付])
)
)
)

M在庫数

在庫数 = 入庫累計 - 出庫累計 です。

M在庫数:=[M入庫累計] - [M出庫累計]

M出庫金額

FIFOで出庫対象範囲と重なる入庫データの範囲を抽出し、その「単価*数量」の合計をSUMXで求めます。仕組みは、コード中のコメント・後述の解説・サンプルファイルの中の図を見てください。
'入庫'または'出庫'データがある日のみ表示します。データが無い日はBLANKです。ピボットテーブルで表示しないか、フィルターやスライサーで表示日の範囲を絞るなら、最後のIFは不要です。

M出庫金額:=VAR vHinmei = IF(HASONEVALUE('品名'[品名]), VALUES('品名'[品名]), "ALL")
VAR vHinmeiList = ALL('品名'[品名])
// =============================================
// 入庫ロットテーブルの構築
// 各ロット(日付×品名×単価)に対して
// 品名内での入庫累計下限・上限を付与する
// =============================================
VAR vNyukoBase =
ADDCOLUMNS(
// 全期間・全品名の入庫レコードを取得
FILTER(ALL('入庫'), NOT(ISBLANK('入庫'[入庫数]))),
        // 入庫累計上限 = 同品名・同日付以前の入庫数合計
"@入庫累計上限",
VAR vThisHinmei = '入庫'[品名]
VAR vThisDate = '入庫'[日付]
RETURN
CALCULATE(
SUM('入庫'[入庫数]),
FILTER(
ALL('入庫'),
'入庫'[品名] = vThisHinmei && '入庫'[日付] <= vThisDate
)
),
        // 入庫累計下限 = 同品名・同日付より前の入庫数合計(なければ0)
"@入庫累計下限",
VAR vThisHinmei = '入庫'[品名]
VAR vThisDate = '入庫'[日付]
RETURN
CALCULATE(
SUM('入庫'[入庫数]),
FILTER(
ALL('入庫'),
'入庫'[品名] = vThisHinmei && '入庫'[日付] < vThisDate
)
) + 0
)
// =============================================
// 出庫トランザクションテーブルの構築
// 各出庫レコードに対して
// 品名内での出庫累計下限・上限を付与する
// (月集計・総計対応のため全出庫をループする)
// =============================================
VAR vSoukoTrans =
ADDCOLUMNS(
// 全期間・全品名の出庫レコードを取得
FILTER(ALL('出庫'), NOT(ISBLANK('出庫'[出庫数]))),
        // 出庫累計上限 = 同品名・同日付以前の出庫数合計
"@出庫累計上限",
VAR vThisHinmei = '出庫'[品名]
VAR vThisDate = '出庫'[日付]
RETURN
CALCULATE(
SUM('出庫'[出庫数]),
FILTER(
ALL('出庫'),
'出庫'[品名] = vThisHinmei && '出庫'[日付] <= vThisDate
)
),
        // 出庫累計下限 = 同品名・同日付より前の出庫数合計(なければ0)
"@出庫累計下限",
VAR vThisHinmei = '出庫'[品名]
VAR vThisDate = '出庫'[日付]
RETURN
CALCULATE(
SUM('出庫'[出庫数]),
FILTER(
ALL('出庫'),
'出庫'[品名] = vThisHinmei && '出庫'[日付] < vThisDate
)
) + 0
)
// ピボットのコンテキストの日付範囲を取得
// 日付1行・月集計・総計のいずれにも対応
VAR vDateMin = MIN('カレンダ'[日付])
VAR vDateMax = MAX('カレンダ'[日付])
// =============================================
// FIFO按分計算
// コンテキストの日付範囲内の出庫トランザクションを
// 1件ずつループし、各件の出庫累計範囲(下限〜上限)に
// 重なる入庫ロットを特定して単価×数量を合算する
// =============================================
VAR vResult =
SUMX(
// コンテキストの日付範囲内かつ対象品名の出庫トランザクションに絞る
FILTER(
vSoukoTrans,
'出庫'[日付] >= vDateMin && '出庫'[日付] <= vDateMax
&& IF(
vHinmei = "ALL",
TRUE(),
'出庫'[品名] = vHinmei
)
),
// 当該出庫トランザクションの品名・累計範囲を取得
VAR vH = '出庫'[品名]
VAR vSouko_Upper = [@出庫累計上限] // 今回までの出庫累計
VAR vSouko_Lower = [@出庫累計下限] // 前回までの出庫累計
RETURN
// 出庫累計範囲に重なる入庫ロットをFIFOで按分
SUMX(
FILTER(
// 対象品名の入庫ロットに絞る
FILTER(vNyukoBase, '入庫'[品名] = vH),
// 出庫累計範囲と入庫累計範囲が重なるロットのみ
[@入庫累計上限] > vSouko_Lower
&& [@入庫累計下限] < vSouko_Upper
),
// 重複数量 = 両範囲の重なり部分
// MIN(当日出庫累計, @当日入庫累計) - MAX(前日出庫累計, @前日入庫累計)
VAR vOverlapQty =
MIN(vSouko_Upper, [@入庫累計上限])
- MAX(vSouko_Lower, [@入庫累計下限])
RETURN
// 重複数量 × 入庫単価
IF(vOverlapQty > 0, vOverlapQty * '入庫'[入庫単価], 0)
)
)
RETURN
IF( // 出庫が無い行はBLANKを返して非表示にする
CALCULATE(COUNTROWS('入庫'), ALL('品名'[品名])) > 0
|| CALCULATE(COUNTROWS('出庫'), ALL('品名'[品名])) > 0,
vResult
)

式の解説

vNyukoBaseは、'入庫'テーブルに該当行の前の日までの品名別の累計(下限)と該当日までの累計(上限)列を追加したものです。
vSoukoTransは、'出庫'テーブルに該当行の前の日までの品名別の累計(下限)と該当日までの累計(上限)列を追加したものです。
vResultでSoukoTranceの1行毎にvNyukoBaseの該当する部分の単価と数量を掛け算したものをSUMXで合計し、更にコンテキストの範囲の合計をSUMXで求めています。

補足

Claudeが提案したコードは、最後のIFを先頭に配置するものでした。これは、計算不要な判断を先にして、パフォーマンスを上げるためです。しかし、IFの真の場合のコードが非常に長く可読性が悪いので、上記のようにIFを最後のRETURNの後に配置しました。パフォーマンスは不明ですが、DAXエンジンの最適化で、評価しない変数は計算されないと思われ、パフォーマンスの低下は少しだけかも知れません?

 

M在庫金額

FIFOで在庫対象範囲と重なる入庫データの範囲を抽出し、その単価*数量の合計を求めます。仕組みは、コード中のコメント・後述の解説・サンプルファイルの中の図を見てください。
'入庫'または'出庫'データがある日のみ表示します。データが無い日はBLANKです。ピボットテーブルで表示しないか、フィルターやスライサーで表示日の範囲を絞るなら、最後のIFは不要です。

M在庫金額:=VAR vMaxDate = MAX('カレンダ'[日付])
// ピボットの品名フィルタを取得。複数品名(集計行)は"ALL"
VAR vHinmei = IF(HASONEVALUE('品名'[品名]), VALUES('品名'[品名]), "ALL")
// =============================================
// 入庫ロットテーブルの構築
// 各ロット(日付×品名×単価)に対して
// 品名内での入庫累計下限・上限を付与する
// =============================================
VAR vNyukoBase =
ADDCOLUMNS(
// 全期間・全品名の入庫レコードを取得
FILTER(ALL('入庫'), NOT(ISBLANK('入庫'[入庫数]))),
        // 入庫累計上限 = 同品名・同日付以前の入庫数合計
"@入庫累計上限",
VAR vThisHinmei = '入庫'[品名]
VAR vThisDate = '入庫'[日付]
RETURN
CALCULATE(
SUM('入庫'[入庫数]),
FILTER(
ALL('入庫'),
'入庫'[品名] = vThisHinmei
&& '入庫'[日付] <= vThisDate
)
),
        // 入庫累計下限 = 同品名・同日付より前の入庫数合計(なければ0)
"@入庫累計下限",
VAR vThisHinmei = '入庫'[品名]
VAR vThisDate = '入庫'[日付]
RETURN
CALCULATE(
SUM('入庫'[入庫数]),
FILTER(
ALL('入庫'),
'入庫'[品名] = vThisHinmei
&& '入庫'[日付] < vThisDate
)
) + 0
)
// =============================================
// FIFO按分計算
// vHinmei="ALL"(集計行)のとき品名ごとにループして合算
// 品名フィルタあり のとき直接計算
// =============================================
VAR vResult =
IF(
vHinmei = "ALL",
        // 全体・総計: 品名ごとにループして在庫金額を合算
SUMX(
ALL('品名'[品名]),
VAR vH = '品名'[品名]
            // 対象品名の入庫累計上限(コンテキスト日付まで)
VAR vNyuko_Upper =
CALCULATE(
SUM('入庫'[入庫数]),
FILTER(ALL('カレンダ'), 'カレンダ'[日付] <= vMaxDate),
'入庫'[品名] = vH
)
            // 対象品名の出庫累計上限(コンテキスト日付まで)
// = FIFOで払い出し済みの数量
VAR vSouko_Upper =
CALCULATE(
SUM('出庫'[出庫数]),
FILTER(ALL('カレンダ'), 'カレンダ'[日付] <= vMaxDate),
'出庫'[品名] = vH
)
            RETURN
// 在庫残ロット = 入庫累計範囲のうち出庫済み範囲より後ろの部分
SUMX(
FILTER(
// 処理対象品名の入庫ロットに絞る
FILTER(vNyukoBase, '入庫'[品名] = vH),
// 出庫済み範囲より後ろかつ入庫済み範囲内のロット
[@入庫累計上限] > vSouko_Upper
&& [@入庫累計下限] < vNyuko_Upper
),
// 重複数量 × 入庫単価
// MIN(当日入庫累計, @当日入庫累計) - MAX(当日出庫累計, @前日入庫累計)
VAR vOverlapQty =
MIN(vNyuko_Upper, [@入庫累計上限])
- MAX(vSouko_Upper, [@入庫累計下限])
RETURN
IF(vOverlapQty > 0, vOverlapQty * '入庫'[入庫単価], 0)
)
),
        // 品名フィルタあり: 対象品名のみ直接計算
VAR vNyuko_Upper = [M入庫累計]
VAR vSouko_Upper = [M出庫累計]
RETURN
SUMX(
FILTER(
// 対象品名の入庫ロットに絞る
FILTER(vNyukoBase, '入庫'[品名] = vHinmei),
// 在庫残ロット = 出庫済み範囲より後ろかつ入庫済み範囲内
[@入庫累計上限] > vSouko_Upper
&& [@入庫累計下限] < vNyuko_Upper
),
// 重複数量 = 両範囲の重なり部分
// MIN(当日入庫累計, @当日入庫累計)-MAX(当日出庫累計, @前日入庫累計)
VAR vOverlapQty =
MIN(vNyuko_Upper, [@入庫累計上限])
- MAX(vSouko_Upper, [@入庫累計下限])
RETURN
// 重複数量 × 入庫単価
IF(vOverlapQty > 0, vOverlapQty * '入庫'[入庫単価], 0)
)
)
RETURN
IF(
CALCULATE(COUNTROWS('入庫'), ALL('品名'[品名])) > 0
|| CALCULATE(COUNTROWS('出庫'), ALL('品名'[品名])) > 0,
vResult
)

式の解説

vNyukoBaseは、'入庫'テーブルに該当行の前の日までの品名別の累計(下限)と該当日までの累計(上限)列を追加したものです。
在庫範囲は出庫累計~入庫累計なので、vResultの中で、在庫下限を出庫累計、在庫上限を入庫累計に設定しています。これを後述の「FIFO按分数量計算の基本的な考え方」の仕組みでvNyukoBaseの範囲を抽出し、該当部分の単価と数量から金額を計算しています。

M在庫金額_指定日

回転数計算用に使用する中間メジャーです。
FIFOで在庫対象範囲と重なる入庫データの範囲を抽出し、その単価*数量の合計を求めます。仕組みは、コード中のコメント・後述の解説・サンプルファイルの中の図を見てください。
回転数を求めるために必要な、データが無い日の在庫金額も計算します。

M在庫金額_指定日:=VAR vHinmei =
IF(HASONEVALUE('品名'[品名]), VALUES('品名'[品名]), "ALL")
// ピボットの品名フィルタを取得。複数品名(集計行)は"ALL"
// =============================================
// M在庫金額_指定日 : コンテキストの在庫金額を求めるメジャー。
// M在庫金額の'入庫'、'出庫'レコード[日付]の限定を解除し、入庫、出庫が無い日も算出。
// ピボットテーブルに表示しない。M在庫金額平均、M回転率算出用。
// =============================================
VAR vThisDate = MAX('カレンダ'[日付])
// =============================================
// 入庫ロットテーブルの構築
// 各ロット(日付×品名×単価)に対して
// 品名内での入庫累計下限・上限を付与する
// =============================================
VAR vNyukoBase =
ADDCOLUMNS(
// 全期間・全品名の入庫レコードを取得
FILTER(ALL('入庫'), NOT(ISBLANK('入庫'[入庫数]))),
        // 入庫累計上限 = 同品名・同日付以前の入庫数合計
"@入庫累計上限",
VAR vThisHinmei = '入庫'[品名]
VAR vD = '入庫'[日付]
RETURN
CALCULATE(
SUM('入庫'[入庫数]),
FILTER(ALL('入庫'),
'入庫'[品名] = vThisHinmei
&& '入庫'[日付] <= vD
)
),
        // 入庫累計下限 = 同品名・同日付より前の入庫数合計(なければ0)
"@入庫累計下限",
VAR vThisHinmei = '入庫'[品名]
VAR vD = '入庫'[日付]
RETURN
CALCULATE(
SUM('入庫'[入庫数]),
FILTER(ALL('入庫'),
'入庫'[品名] = vThisHinmei
&& '入庫'[日付] < vD
)
) + 0
)
RETURN
// =============================================
// FIFO按分計算
// vHinmei="ALL"(集計行)のとき品名ごとにループして合算
// 品名フィルタあり のとき直接計算
// =============================================
IF(
vHinmei = "ALL",
// 全体・総計: 品名ごとにループして在庫金額を合算
SUMX(
ALL('品名'[品名]),
VAR vH = '品名'[品名]
       // 対象品名の入庫累計上限(コンテキスト日付まで)
VAR vNyuko_Upper =
CALCULATE(
SUM('入庫'[入庫数]),
FILTER(ALL('入庫'),
'入庫'[品名] = vH
&& '入庫'[日付] <= vThisDate
)
)
        // 対象品名の出庫累計上限(コンテキスト日付まで)
// = FIFOで払い出し済みの数量
VAR vSouko_Upper =
CALCULATE(
SUM('出庫'[出庫数]),
FILTER(ALL('出庫'),
'出庫'[品名] = vH
&& '出庫'[日付] <= vThisDate
)
) + 0
        RETURN
// 在庫残ロット = 入庫累計範囲のうち出庫済み範囲より後ろの部分
SUMX(
FILTER(
// 処理対象品名の入庫ロットに絞る
FILTER(vNyukoBase, '入庫'[品名] = vH),
// 出庫済み範囲より後ろかつ入庫済み範囲内のロット
[@入庫累計上限] > vSouko_Upper
&& [@入庫累計下限] < vNyuko_Upper
),
// 重複数量 = 両範囲の重なり部分
// MIN(当日入庫累計, @当日入庫累計) - MAX(当日出庫累計, @前日入庫累計)
VAR vOverlapQty =
MIN(vNyuko_Upper, [@入庫累計上限])
- MAX(vSouko_Upper, [@入庫累計下限])
RETURN
// 重複数量 × 入庫単価
IF(vOverlapQty > 0, vOverlapQty * '入庫'[入庫単価], 0)
)
),
    // 品名フィルタあり: 対象品名のみ直接計算
// 入庫累計
VAR vNyuko_Upper =
CALCULATE(
SUM('入庫'[入庫数]),
FILTER(ALL('入庫'),
'入庫'[品名] = vHinmei
&& '入庫'[日付] <= vThisDate
)
)
// 出庫累計
VAR vSouko_Upper =
CALCULATE(
SUM('出庫'[出庫数]),
FILTER(ALL('出庫'),
'出庫'[品名] = vHinmei
&& '出庫'[日付] <= vThisDate
)
) + 0
RETURN
SUMX(
FILTER(
// 対象品名の入庫ロットに絞る
FILTER(vNyukoBase, '入庫'[品名] = vHinmei),
// 在庫残ロット = 出庫済み範囲より後ろかつ入庫済み範囲内
[@入庫累計上限] > vSouko_Upper
&& [@入庫累計下限] < vNyuko_Upper
),
// 重複数量 = 両範囲の重なり部分
// MIN(当日入庫累計, @当日入庫累計)-MAX(当日出庫累計, @前日入庫累計)
VAR vOverlapQty =
MIN(vNyuko_Upper, [@入庫累計上限])
- MAX(vSouko_Upper, [@入庫累計下限])
RETURN
// 重複数量 × 入庫単価
IF(vOverlapQty > 0, vOverlapQty * '入庫'[入庫単価], 0)
)
)


式の解説

M在庫金額とほぼ同じです。主な違いは次の2点です。これにより、全日の在庫金額を算出できるようにし、後述のM在庫金額をで利用できるようにしています。

  • 品名が1つの場合に、在庫範囲の上下限を用いる式に[M入庫累計]と[M出庫累計]を使用せず、累計計算式を記述している。(理由 : [M入庫累計]と[M出庫累計]は入出庫が無い日の値がBLANKなので、後の[M在庫金額平均]に使えないため。)
  • '入庫'と'出庫'のレコードに限定するIF文が無い。(補足:VAR vResutを使用せずに、直接RETURNの下にvResultにあったコードを書いています。)

M在庫金額平均A

回転数計算用に使用する中間メジャーです。
計算対象範囲の全ての日の在庫金額の平均値を求めます。
単日では計算しません。(単日の回転率が必要であれば、IFを削除してください。)

M在庫金額平均A:=IF(
NOT(HASONEVALUE('カレンダ'[日付])),
AVERAGEX(
'カレンダ',
VAR vThisDate = 'カレンダ'[日付]
RETURN
CALCULATE(
[M在庫金額_指定日],
FILTER(ALL('カレンダ'), 'カレンダ'[日付] = vThisDate)
)
)
)
式の解説

コンテキストの範囲の'カレンダ'のM在庫金額_指定日をAVERAGEXで平均しています。CALCULATEのFILTERでAVRAGEXの行コンテキストである日付をvThisDateで参照して、日付毎のM在庫金額_指定日を計算しています。
ピボットテーブルに表示しないのであれば、IFは不要です。

M在庫金額平均B

回転率計算用に使用する中間メジャーです。
M在庫金額平均A = (計算対象範囲の初日の在庫 + 計算対象範囲の最終日の在庫) / 2
この式は、回転率の計算に一般的に使われているようです。但し、入庫、出庫の時期が偏っている場合は、誤差が大きくなります。
単日では計算しません。(単日の回転率が必要であれば、IFを削除してください。)

M在庫金額平均B:=IF(
NOT(HASONEVALUE('カレンダ'[日付])),
VAR vDateMin = MIN('カレンダ'[日付])
VAR vDateMax = MAX('カレンダ'[日付])
    // 期首時点の在庫金額
VAR vStart =
CALCULATE(
[M在庫金額_指定日],
FILTER(ALL('カレンダ'), 'カレンダ'[日付] = vDateMin)
)
// 期末時点の在庫金額
VAR vEnd =[M在庫金額平均B]
CALCULATE(
[M在庫金額_指定日],
FILTER(ALL('カレンダ'), 'カレンダ'[日付] = vDateMax)
)
RETURN
DIVIDE(vStart + vEnd, 2)
)

M回転率A

対象期間の全日の在庫平均を用いた回転率です。M回転率A = 対象期間の出庫金額合計 / 対象期間の在庫金額の平均
M回転率はコンテキストの対象期間の回転率で、財務管理で使われるようです。

M回転率A:=DIVIDE([M出庫金額], [M在庫金額平均A])

M回転率B

初日と最終日の在庫だけを用いた回転率です。M回転率B = 対象期間の出庫金額合計 / (対象期間の初日在庫金額 + 最終日在庫金額) / 2

M回転率B:=DIVIDE([M出庫金額], [M在庫金額平均B])

 

M月回転率A

M月回転率は月当たりの回転率で、生産管理で使われるようです。式は次の通りです。
M月回転率A = M回転率A / コンテキストの月数

M月回転率A :=
IF(
NOT(HASONEVALUE('カレンダ'[日付])),
// コンテキストの月数を取得
VAR vMonthCount =
DISTINCTCOUNT('カレンダ'[年月])
RETURN
DIVIDE([M回転率A], vMonthCount)
)

M在庫日数A

式は、「在庫日数 = 月の日数 / 月回転率 = (日数 / 月数) / 月回転率」です。

M在庫日数A :=
IF(
NOT(HASONEVALUE('カレンダ'[日付])),
// コンテキストの月数を取得
VAR vMonthCount =
DISTINCTCOUNT('カレンダ'[年月])
// コンテキストの総日数を取得
VAR vDayCount =
DISTINCTCOUNT('カレンダ'[日付])
// 月当たりの平均日数
VAR vDaysPerMonth =
DIVIDE(vDayCount, vMonthCount)
RETURN
// 在庫日数 = 月当たり日数 / 月回転率
DIVIDE(vDaysPerMonth, [M月回転率A])
)


区間の重なり判定と重複数量の算出方法の解説

在庫や出庫に対応する入庫情報(単価と数量)の取得の仕組みを解説します。
M出庫金額メジャーのVAR vResultの次のコードの動作を例として解説します。次のコードは日付と品名のフィルタのコードを省略しています。

VAR vResult =
SUMX(
vSoukoTrans,
VAR vSouko_Upper = [@出庫累計上限] // 今回までの出庫累計
VAR vSouko_Lower = [@出庫累計下限] // 前回までの出庫累計
RETURN
// 出庫累計範囲に重なる入庫ロットをFIFOで按分
SUMX(
FILTER( // 対象品名の入庫ロットに絞る
vNyukoBase,
// 出庫累計範囲と入庫累計範囲が重なるロットのみ
[@入庫累計上限] > vSouko_Lower && [@入庫累計下限] < vSouko_Upper
),
// 重複数量 = 両範囲の重なり部分
// MIN(当日出庫累計, @当日入庫累計) - MAX(前日出庫累計, @前日入庫累計)
VAR vOverlapQty =
MIN(vSouko_Upper, [@入庫累計上限]) - MAX(vSouko_Lower, [@入庫累計下限])
RETURN
// 重複数量 × 入庫単価
IF(vOverlapQty > 0, vOverlapQty * '入庫'[入庫単価], 0)
)
)

vSoukoTrans

日付 品名 出庫数 @出庫累計上限 @出庫累計下限
04/05 品A 40 40 0
04/05 品B 2 2 0
05/10 品B 6 2+6=8 2
05/20 品A 40 40+40=80 40

vNyukoBase

日付 品名 入庫単価 入庫数 @入庫累計上限 @入庫累計下限
04/01 品A 1 50 50 0
04/01 品B 10 5 5 0
04/15 品A 2 10 50+10=60 50
05/01 品B 20 5 5+5=10 5
05/05 品A 3 50 50+10+50=110 50+10=60

FIFO按分数量計算の基本的な考え方

入庫ロットと出庫範囲を数直線上の区間として考えます。

0────────────────▶ 累計数量
[入庫ロットの区間]
[ 出庫範囲 ]

2つの区間が重なる部分が、そのロットから出庫される数量です。

2つの区間 [A下限, A上限] と [B下限, B上限] が重なる条件は以下です。但し、下限は対象区間の直前の値とします。なので、実際の対象期間は下限+1日から始まります。以下の図では"|"で示す位置になります。

A上限 > B下限  かつ  A下限 < B上限

重なる部分の長さは、次の式で求まります。

MIN(A上限, B上限) - MAX(A下限, B下限)
A上限 > B下限
0 1 2 3 4 5 6 7 8 9 10 11 12
al--|───────-AU A上限
bl--|───────────-bu B下限 : ✓ MIN(AU,bu) - MAX(al,bl) = 5
BL--|───────-bu B下限 : ✓ MIN(AU,bu) - MAX(al,BL) = 5
BL--|───────-bu B下限 : ✓ MIN(AU,bu) - MAX(al,BL) = 2
BL--|───────-bu B下限 : ✓ MIN(AU,bu) - MAX(al,BL) = 1
BL--|───────-bu B下限 : ❌ A上限AU > B下限BL = False
A下限 < B上限
0 1 2 3 4 5 6 7 8 9 10 11 12
AL--|───────-au A下限
bl--|───────────-bu B上限 : ✓ MIN(au,bu) - MAX(AL,bl) = 5
bl--|───────-BU B上限 : ✓ MIN(au,BU) - MAX(AL,bl) = 5
bl--|───────-BU B上限 : ✓ MIN(au,BU) - MAX(AL,bl) = 2
bl--|───────-BU B上限 : ✓ MIN(au,BU) - MAX(AL,bl) = 1
bl--|───────-BU B上限 : ❌ A下限AL < B上限BU = False

これをFILTERとvOverlapQtyに当てはめると以下になります。

入庫ロット区間  : [@入庫累計下限, @入庫累計上限]
出庫範囲区間 : [vSouko_Lower, vSouko_Upper ]

重なり条件:

[@入庫累計上限] > vSouko_Lower (入庫上限が出庫下限より大きい)
[@入庫累計下限] < vSouko_Upper (入庫下限が出庫上限より小さい)

重複数量:

MIN(vSouko_Upper, [@入庫累計上限]) - MAX(vSouko_Lower, [@入庫累計下限])

例1で視覚化(品B 05/10出庫5)

vSouko_Lower=5, vSouko_Upper=10

0   1   2   3   4   5   6   7   8   9   10
al--|───────-AU 04/01ロット[0-5]
AL--|───────-AU 05/01ロット[5-10]
BL--|───────-BU 出庫範囲[5-10]

04/01ロット[0-5] vs 出庫範囲[5-10]

重なり条件:
[@入庫累計上限]5 > vSouko_Lower5 -> False ❌
→ 重なりなし(上限と下限がちょうど接しているだけ)

05/01ロット[5-10] vs 出庫範囲[5-10]

重なり条件:
[@入庫累計上限]10 > vSouko_Lower5 -> True ✓
[@入庫累計下限]5 < vSouko_Upper10 -> True ✓
→ 重複数量 = MIN(10,10) - MAX(5,5) = 10-5 = 5個 × ¥20 = ¥100

例2で視覚化(品B 05/10出庫6)

vSouko_Lower=2, vSouko_Upper=8

0   1   2   3   4   5   6   7   8   9   10
AL--|───────-AU 04/01ロット[0-5]
AL--|───────-AU 05/01ロット[5-10]
BL--|─────────-BU 出庫範囲[2-8]

04/01ロット[0-5] vs 出庫範囲[2-8]

重なり条件:
[@入庫累計上限]5 > vSouko_Lower2 -> True ✓
[@入庫累計下限]0 < vSouko_Upper8 -> True ✓
→ 重複数量 = MIN(8,5) - MAX(2,0) = 5-2 = 3個 × ¥10 = ¥30

05/01ロット[5-10] vs 出庫範囲[2-8]

重なり条件:
[@入庫累計上限]10 > vSouko_Lower2 -> True ✓
[@入庫累計下限]5 < vSouko_Upper8 -> True ✓
→ 重複数量 = MIN(8,10) - MAX(2,5) = 8-5 = 3個 × ¥20 = ¥60

合計出庫金額 = ¥30 + ¥60 = ¥90

例3で視覚化(品B 05/10出庫3)

vSouko_Lower=2, vSouko_Upper=5

0   1   2   3   4   5   6   7   8   9   10
AL--|───────-AU 04/01ロット[0-5]
AL--|───────-AU 05/01ロット[5-10]
BL--|───-| 出庫範囲[2-5]

04/01ロット[0-5] vs 出庫範囲[2-5]

重なり条件:
[@入庫累計上限]5 > vSouko_Lower2 -> True ✓
[@入庫累計下限]0 < vSouko_Upper5 -> True ✓
→ 重複数量 = MIN(5,5) - MAX(2,0) = 5-2 = 3個 × ¥10 = ¥30

05/01ロット[5-10] vs 出庫範囲[2-5]

重なり条件:
[@入庫累計上限]10 > vSouko_Lower2 -> True ✓
[@入庫累計下限]5 < vSouko_Upper5 -> False ❌
→ 重なりなし(出庫上限と入庫下限がちょうど接しているだけ)

合計出庫金額 = ¥30

まとめ:境界値の扱いがポイント

区間の下限は、対象区間の直前の値としています。
なので、区間の端がちょうど接している場合は重なりなしとして扱います。

条件 意味
上限 > 下限(厳密な不等号) 接しているだけでは重なりとみなさない
MIN - MAX > 0のときのみ計上 重複数量が正のときのみ金額計算
これにより同じ個体が二重にカウントされることを防いでいます。  

「区間の重なり判定と重複数量の算出方法の解説」は、ここまでです。


8) ピボットテーブルの設定

ピボットテーブルのフィールドで、必要な項目を設定します。
列 : '品名'[品名]、Σ値。
行 : 'カレンダ'[年月]、'カレンダ'[日付]。
Σ値 : ['M入庫単価]、'入庫'[入庫数]、'出庫'[出庫数]、[M在庫数]、[M入庫金額]、[M出庫金額]、[M在庫金額]、[回転数A]。

 

5. サンプル

「2. 入力」に示すように、'入庫'表に単価があるケースのサンプルファイルです。

ダウンロード - 260515zaikokin.xlsx

「6. 単価表が独立しているケース」です[Rev.1]。更に、作業用の中間メジャーを使用して、式を短くしています[Rev.2]。メジャーの式と動作説明は、サンプルファイルのコード中のコメントと図のシートを見てください。

ダウンロード - 260515zaikokinr2.xlsx

 

6. 単価表が独立しているケース

1) 入力表

テーブル名:入庫

日付 品名 入庫数
26/04/01 品A 50
26/04/01 品B 5
26/04/15 品A 10
26/05/01 品B 5
26/05/05 品A 50

テーブル名:単価

適用開始日 品名 単価
26/04/01 品A 1
26/04/01 品B 10
26/04/15 品A 2
26/05/01 品B 20
26/05/05 品A 3

その他は、「2. 入力」と同じです。

2) リレーション

'単価'[品名] *:1 '品名'[品名]

参考 : [適用開始日]は、それ以降の複数の[日付]に対応するので、リレーションを設定しません。メジャーで検索します。

3) メジャー

詳細は、「5.サンプル」の「260515zaikokinR2.xlsx」を見てください。

M入庫単価_ロット

'入庫'に適用するイテレータ関数から呼び出されるメジャーです。'入庫'の行コンテキストに対応する'単価'[単価]を返します。

M入庫単価_ロット:=VAR vDate = MAX('入庫'[日付]) // 呼出し元は'入庫'を処理するイテレータの中
// 入庫日・品名に対応する単価を単価テーブルから取得[Rev.1]
RETURN
CALCULATE(
MAX('単価'[単価]),
FILTER( // 入庫日以前の最大の適用開始日でフィルタ
ALL('単価'[適用開始日]),
'単価'[適用開始日] = CALCULATE(
MAX('単価'[適用開始日]), // 入庫日以前の最大の適用開始日を取得
FILTER(
ALL('単価'[適用開始日]),
'単価'[適用開始日] <= vDate // 入庫日以前でフィルタ
)
)
)
)

M入庫金額

M入庫金額:=SUMX(
'入庫',
[M入庫単価_ロット] * '入庫'[入庫数]
)

式の解説

SUMXやADDCOLUMNSなどのイテレータは、各行を処理するとき行コンテキストを生成します。その行コンテキスト上でメジャーを呼び出すと、メジャー内のCALCULATEが暗黙のコンテキスト遷移を起こし、行コンテキストをフィルタコンテキストに変換します。

'入庫'[日付]=26/04/01, '入庫'[品名]=品A の入庫金額の例

SUMX('入庫', [M入庫単価_ロット] * '入庫'[入庫数])
       ↓
各行で '入庫'[日付]=26/04/01, '入庫'[品名]=品A の行コンテキストが生成
       ↓
[M入庫単価_ロット]内のCALCULATEがコンテキスト遷移
       ↓
'入庫'[日付]=26/04/01, '入庫'[品名]=品A のフィルタコンテキストに変換
       ↓
VAR vDate = MAX('入庫'[日付]) → 26/04/01 が取得される
'品名'[品名]コンテキスト → 品A でフィルタされる

M出庫金額、M在庫金額、M在庫金額_指定日

式の中の「VAR vNyukoBase =」 に"@単価"を追加します。そして、'入庫'[入庫単価]を[@単価]に書き換えます。

// =============================================
// 入庫ロットテーブルの構築
// 各ロット(日付×品名×単価)に対して
// 品名内での入庫累計下限・上限を付与する
// =============================================
VAR vNyukoBase =
ADDCOLUMNS(
FILTER(ALL('入庫'), NOT(ISBLANK('入庫'[入庫数]))),
// 入庫累計上限
"@入庫累計上限",
VAR vThisHinmei = '入庫'[品名]
VAR vThisDate = '入庫'[日付]
RETURN
CALCULATE(
SUM('入庫'[入庫数]),
FILTER(ALL('入庫'),
'入庫'[品名] = vThisHinmei
&& '入庫'[日付] <= vThisDate
)
),
// 入庫累計下限
"@入庫累計下限",
VAR vThisHinmei = '入庫'[品名]
VAR vThisDate = '入庫'[日付]
RETURN
CALCULATE(
SUM('入庫'[入庫数]),
FILTER(ALL('入庫'),
'入庫'[品名] = vThisHinmei
&& '入庫'[日付] < vThisDate
)
) + 0,
// 入庫日・品名に対応する単価を単価テーブルから取得[Rev.1]
"@単価",
[M入庫単価_ロット]
)

// 重複数量 × 入庫単価
IF(vOverlapQty > 0, vOverlapQty * '入庫'[入庫単価], 0)

↓ 次のように変更します。

// 重複数量 × 入庫単価
IF(vOverlapQty > 0, vOverlapQty * [@単価],0) // '単価'[単価]から[Rev.1]


7. 関連動画

8. 余談

1) AI/LLMの利用

FIFOの重なり算出の方法は、Claudeが出したものです。
しかし、Claudeが生成したコードは、エラーや間違った値を算出するので、そのままでは使えません。でも、考え方の参考になります。そこで、回答されたコードを調べ、間違いを探し、正しい方向へ修正指示をすることで正解を得ます。
Claudeは、色々なケースを考慮し制限を掛ける条件式を入れる印象です。この条件が重複することがあり、コードが長く冗長になります。Claudeに不要なコードとその理由を知らせることで、コードを短くします。

一方、ChatGPT、Copilot、Geminiは、最適化で必要な条件まで外してしまい、間違ったコードになる印象です。LLMは、アルゴリズムで答えを出しているわけでは無く、似たような事例から解を導きだすので、間違いが多いようです。
Claudeは間違いを指摘すると、問題の切り分けをするためのデバッグコードを提示し、その結果を受けてアルゴリズムを修正していく印象です。

しかし、Claudeが優秀ということも無く、Geminiなどの方が適切なコードを得られることもあるので、気に入らない回答であれば、同じ質問を他のAI/LLMに入力するのが良いです。

AI/LLMの回答は、信頼性の検証が必要です。メジャーなどのコードや数式であれば、正解が分かっているサンプルデータでのテストと、コードレビューをします。テストでは見つからないバグが、コードレビューで見つかることがあります。本記事のコードも、Claudeのコードの動作を理解したうえで修正して掲載しています。但し、ごく簡単なテストしかしていません。

2) ワークシート式/VBA/Power Pivot

ワークシート式が得意な方は、それでも良いと思いますが、可読性が悪く、間違いを起こしやすいです。構造化参照を使い、+N()でコメントを入れれば、可読性が上がります。

VBAやPythonが得意な方は、それが良いと思います。VBAやPythonとDAXの可読性は、似たようなものだと思います。

Power Pivotの利点は、ピボットテーブルなので、コードや式を変更せずに項目の変更や選択フィルター処理ができる事です。但し、メジャーは、コンテキストやイテレータ関数の理解が必要です。また、Excel/Power BIにはメジャーのデバッグ機能が無いので、部分的なテストコードを書いてデバッグします。

このメジャーはPower BIでも動作するので、Power BIのドリルスルーや分析機能で回転率の悪化要因などの調査に活用することもできると思います。

| | コメント (0)

完成数から納期を満たす納期と注残を計算 Excel Power Pivot

1. 前置き

Yahoo!知恵袋に、品名と完成数から引当を開始できる納期と注残を算出する質問がありました。目的は、注文の消化進捗を確認するためです。
https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q13327903548

ワークシート式の回答が付いています。
単純なメジャーでも求めることができます。そのメジャーに多少追加をして、推移と遅延日数を出すようにしました。

なお、あまりテストをしていません。もし、使用される場合は、検証を行い自己責任でお願いいたします。メジャーの動作を理解できない方は、使用を避けてください。

2. 入力

テーブル名 : 注文

品名 注文数 納期
BASE 2 26/05/11
RING 2 26/05/12
BASE 2 26/05/15
RING 1 26/05/31
BASE 3 26/06/02
RING 1 26/06/07
BASE 4 26/06/09
RING 5 26/06/20

260512order1 

テーブル名:完成

日付 BASE RING
26/05/01 2  
26/05/15   1
26/05/15 2  
26/06/01   2
26/06/01 2  
26/06/05   1
26/06/10 2  

260512order2 

ここではクロス表にしましたが、注文と同じようなリストにできるなら処理が簡単になります。

テーブル名:カレンダ

日付 年月 四半期 半期 年度 年度. 四半期. 期 月 年
26/04/01 26/04 26/1Q 26/1H 26年度 2026 1Q 上期 4月 2026
26/04/02 26/04 26/1Q 26/1H 26年度 2026 1Q 上期 4月 2026
省略                  
28/03/31 28/03 27/4Q 27/2H 27年度 2027 4Q 下期 3月 2027

260512order3 

  1. Excelで項目行と空の1行の表を作成し、その範囲を全選択し[挿入]-[テーブル]でテーブル化し、[テーブルデザイン]でテーブル名を`カレンダ'に変更します。
  2. 日付セルをA2とした場合の数式を次に示します。この式を必要な日付範囲まで下にコピーします。
A3 = 最初の日付(シリアル値)
A4 = A3 + 1
B3 = TEXT([@日付], "yy/mm")
C3 = LET(x, EDATE([@日付], -3), (YEAR(x)-2000) & "/" & (INT((MONTH(x) -1) / 3) + 1) & "Q")
D3 = LET(x, EDATE([@日付], -3), (YEAR(x) - 2000) & "/" & (INT((MONTH(x) - 1) / 6) + 1) & "H")
E3 = YEAR(EDATE([@日付], -3)) - 2000 & "年度"
F3 = YEAR(EDATE([@日付], -3))
G3 = INT((MONTH(EDATE([@日付], -3)) - 1) / 3) + 1 & "Q"
H3 = IF(MONTH(EDATE([@日付], -3)) < 7, "上期", "下期")
I3 = TEXT([@日付], "m月")
J3 = YEAR([@日付])

3. 出力

最新

品名 合計 / 注文数 合計 / 完成数 M引当開始_完成 M引当完了_完成 M注残
BASE 11 8 26/06/09 26/06/02 3
RING 9 4 26/06/07 26/06/07 5

260512order4 

Yahoo!知恵袋の仕様は、引き当てが始まった納期ですが、引き当てが完了した日も表示しています。

推移

  BASE RING
年月 日付 合計/注文数 合計/完成数 M注文累計 M完成累計 M注残 M遅延日数_納期 M引当完了_納期 合計/注文数 合計/完成数 M注文累計 M完成累計 M注残 M遅延日数_納期 M引当完了_納期
26/05 26/05/01   2   2 -2                  
26/05/11 2   2 2 0 -10 26/05/01              
26/05/12     2 2 0     2   2   2 +20 26/06/01
26/05/15 2 2 4 4 0 ±0 26/05/15   1 2 1 1    
26/05/31     4 4 0     1   3 1 2 +1 26/06/01
26/05 集計 4 4 4 4 0     3 1 3 1 2    
26/06 26/06/01   2 4 6 -2       2 3 3 0    
26/06/02 3   7 6 1 +8 26/06/10     3 3 0    
26/06/05     7 6 1       1 3 4 -1    
26/06/07     7 6 1     1   4 4 0 -2 26/06/05
26/06/09 4   11 6 5 +2 未完了     4 4 0    
26/06/10   2 11 8 3         4 4 0    
26/06/20     11 8 3     5   9 4 5    
26/06 集計 7 4 11 8 3     6 3 9 4 5    
総計 11 8 11 8 3     9 4 9 4 5    

260512order5 
引当が完了していない納期の遅延日数は最後の完成日の1日後からの差分で求めています。プラスが遅延です。

4. 作成手順概要

1) '注文'をデータモデルに追加

  1. 注文表を全選択し、[挿入]-[テーブル]でテーブル化し、[テーブルデザイン]で名前を"注文"に変更する。
  2. テーブルを選択し、[データ]-[テーブルまたは範囲から]で、Power Queryエディタに取込む。
  3. "納期"の左側の型アイコンをクリックし、[日付]。 ⇒ 型が日付になる。
  4. [ホーム]-[閉じて読み込むv]-[閉じて次に読み込む]で、"接続の作成のみ"と"このモデルをデータモデルに追加する"を選択し[OK]。

2) '完成'をリスト形式にしデータモデルに追加

  1. 注文表を全選択し、[挿入]-[テーブル]でテーブル化し、[テーブルデザイン]で名前を"完成"に変更する。
  2. テーブルを選択し、[データ]-[テーブルまたは範囲から]で、Power Queryエディタに取込む。
  3. "日付"の左側の型アイコンをクリックし、[日付]。 ⇒ 型が日付になる。
  4. "日付"列を選択し、右クリックで[その他の列のピボット解除]。 ⇒ リスト形式になる。
  5. 数式バーの数式を次のように修正する。
= Table.UnpivotOtherColumns(変更された型, {"日付"}, "属性", "値")
↓
= Table.UnpivotOtherColumns(変更された型, {"日付"}, "品名", "完成数")
  1. [ホーム]-[閉じて読み込むv]-[閉じて次に読み込む]で、"接続の作成のみ"と"このモデルをデータモデルに追加する"を選択し[OK]。

3) 'カレンダ'をデータモデルに追加

  1. 'カレンダ'テーブルを選択し、[データ]-[テーブルまたは範囲から]で、Power Queryエディタに取込む。
  2. "日付"の左側の型アイコンをクリックし[日付]。 ⇒ 型が日付になる。
  3. [ホーム]-[閉じて読み込むv]-[閉じて次に読み込む]で、"接続の作成のみ"と"このモデルをデータモデルに追加する"を選択し[OK]。

4) '品名'を生成し、データモデルに追加

  1. 左の'注文'クエリを右クリックし、[参照]で、右の名前を"品名"に変更する。 ⇒ '品名'クエリができる。
  2. "品名"列を右クリックし、[他の列の削除]。
  3. "品名"列を右クリックし、[重複の削除]。
  4. [ホーム]-[閉じて読み込むv]-[閉じて次に読み込む]で、"接続の作成のみ"と"このモデルをデータモデルに追加する"を選択し[OK]。

5) リレーション

[データ]-[データモデル(緑アイコン)v]-[データモデルの管理]-[ホーム]-[ダイアグラムビュー]で、次のように項目をドラッグし、リレーションを取る。
'注文'[納期] *:1 'カレンダ'[日付]
'完成'[日付] *:1 'カレンダ'[日付]
'注文'[品名] *:1 '品名'[品名]
'完成'[品名] *:1 '品名'[品名]
260512order6

6) ピボットテーブルの作成

  1. [挿入]-[ピボットテーブルv]-[データモデルから]。
  2. [ピボットテーブル分析]-[ピボットテーブルv]-[オプション]-[レイアウトと書式]で、"更新時に列幅を自動調整する"のチェックを外す。
  3. [デザイン]-[レポートのレイアウト]-[表形式で表示]。

7) メジャー(DAX式)

ピボットテーブルのフィールドの'注文'または'完成'を右クリックし、[メジャーの追加]で次のメジャーを作成する。
以下、:=の左がメジャーの名前で、:=の右以降がメジャーの式です。

注文数の累計

M注文累計:=VAR ExistReqDay = NOT(ISBLANK(CALCULATE(MAX('注文'[納期]), ALL('品名'[品名]))))    // ピボットテーブルに他の品名も含め'注文'[納期]があるか
VAR ExistCompDay = NOT(ISBLANK(CALCULATE(MAX('完成'[日付]), ALL('品名'[品名])))) // ピボットテーブルに他の品名も含め'完成'[日付]があるか
// ↑推移表に表示する日を制限する。全ての日に累計を表示するならこのコードと次のIFの条件は不要。
RETURN
IF(
ExistReqDay || ExistCompDay, // '注文'[納期]または'完成'[日付]があるか
CALCULATE(
SUM('注文'[注文数]), // FILTERされた[注文数]を合計=注文数の累計
FILTER(
ALL('カレンダ'[日付]), // 累計を計算するためピボットテーブルの日付コンテキストフィルタを解除
'カレンダ'[日付] <= MAX('カレンダ'[日付]) // ピボットテーブルの日付以前の日付を抽出
)
)
)

完成数の累計

M完成累計:=VAR ExistReqDay = NOT(ISBLANK(CALCULATE(MAX('注文'[納期]), ALL('品名'[品名]))))    // ピボットテーブルに他の品名も含め'注文'[納期]があるか
VAR ExistCompDay = NOT(ISBLANK(CALCULATE(MAX('完成'[日付]), ALL('品名'[品名])))) // ピボットテーブルに他の品名も含め'完成'[日付]があるか
// ↑推移表に表示する日を制限する。全ての日に累計を表示するならこのコードと次のIFの条件は不要。
RETURN
IF(
ExistReqDay || ExistCompDay, // '注文'[納期]または'完成'[日付]があるか
CALCULATE(
SUM('完成'[完成数]), // FILTERされた[完成数]を合計=完成数の累計
FILTER(
ALL('カレンダ'[日付]), // 累計を計算するためピボットテーブルの日付コンテキストフィルタを解除
'カレンダ'[日付] <= MAX('カレンダ'[日付]) // ピボットテーブルの日付以前の日付を抽出
)
)
)

注残

= [M注文累計] - [M完成累計]

完成日に引当開始日を表示するメジャー

M引当開始_完成:=VAR c=[M完成累計]    // ピボットテーブルの日の完成累計
RETURN
CALCULATE(
MIN('注文'[納期]), // FILTER条件を満たす最初の日
FILTER(
ALL('カレンダ'[日付]), // 全カレンダを対象に次の条件を日付を抽出
// 前日注文累計 < ピボットテーブルの日の完成累計 && 当日注文数 >= ピボットテーブルの日の完成累計
[M注文累計] - '注文'[合計 / 注文数] < c && [M注文累計] >= c
)
)

完成日に引当完了日を表示するメジャー

M引当完了_完成:=VAR c=[M完成累計]    // ピボットテーブルの日の完成累計
RETURN
CALCULATE(
MAX('注文'[納期]), // FILTER条件を満たす最後の日
FILTER(
ALL('カレンダ'[日付]), // 全カレンダを対象に次の条件を日付を抽出
[M注文累計] <= c // 当日注文数 <= ピボットテーブルの日の完成累計
)
)

参考

以上、5つのメジャーのIFは、Yahoo!知恵袋の最新だけを表示するのであれば、不要です。なので、ワークシート式より分かりやすいと思います。IFは、ピボットテーブルに[日付]を表示して集計行を表示する場合に、データが無い日付を表示しないようにするために付けています。

納期行に表示する遅延日数

M遅延日数_納期:=VAR CurrentNoki = MAX('注文'[納期])    // ピボットテーブルの[納期]
// '完成'を[品名]でフィルタし[日付]でグループ化し[完成数]の合計表を作る
VAR CompListAll =
SUMMARIZE(
FILTER(ALL('完成'), '完成'[品名] = MAX('品名'[品名])),
'完成'[日付],
"@完成数", SUM('完成'[完成数])
)
// 上記表に[完成数]の累計列を追加する
VAR CompTableAll =
ADDCOLUMNS(
CompListAll,
"@完成累計",
VAR ThisDate = '完成'[日付]
RETURN
SUMX(FILTER(CompListAll, '完成'[日付] <= ThisDate), [@完成数])
)
// 全品名の完成最大日(品名フィルタなし)未完成の遅延日数はこれとの差分+1日とする
VAR MaxCompDate =
CALCULATE(MAX('完成'[日付]), ALL('完成'))
// 当該納期を消化した完成日を特定
VAR CompDate =
MINX(
FILTER(
CompTableAll,
[@完成累計] >= [M注文累計]
),
'完成'[日付]
)
// 遅延日数計算
VAR DelayDays =
IF(
NOT(ISBLANK(CompDate)),
// 完成済み:納期より前完成はマイナス、後完成はプラス
IF(
CurrentNoki > CompDate,
-DATEDIFF(CompDate, CurrentNoki, DAY),
DATEDIFF(CurrentNoki, CompDate, DAY)
),
// 未完成かつ納期が全体完成最大日以前
IF(
NOT(ISBLANK(MaxCompDate)) && CurrentNoki <= MaxCompDate,
DATEDIFF(CurrentNoki, MaxCompDate, DAY) + 1
)
)
RETURN
IF( // 単日の納期があるところのみに表示
NOT(ISBLANK(CurrentNoki)) && HASONEVALUE('カレンダ'[日付]),
DelayDays
)

260512order7

納期行に表示する引当完了日

M引当完了_納期:=VAR CurrentNoki = MAX('注文'[納期])    // ピボットテーブルの[納期]
// '完成'を[品名]でフィルタし[日付]でグループ化し[完成数]の合計表を作る
VAR CompListAll =
SUMMARIZE(
FILTER(ALL('完成'), '完成'[品名] = MAX('品名'[品名])),
'完成'[日付],
"@完成数", SUM('完成'[完成数])
)
// 上記表に[完成数]の累計列を追加する
VAR CompTableAll =
ADDCOLUMNS(
CompListAll,
"@完成累計",
VAR ThisDate = '完成'[日付]
RETURN
SUMX(FILTER(CompListAll, '完成'[日付] <= ThisDate), [@完成数])
)
// 全品名の完成最大日(品名フィルタなし)未完成の遅延日数はこれとの差分+1日とする
VAR MaxCompDate =
CALCULATE(MAX('完成'[日付]), ALL('完成'))
// 当該納期を消化した完成日を特定
VAR CompDate =
MINX(
FILTER(
CompTableAll,
[@完成累計] >= [M注文累計]
),
'完成'[日付]
)
RETURN
IF( // 単日の納期があるところのみに表示
NOT(ISBLANK(CurrentNoki)) && HASONEVALUE('カレンダ'[日付]),
IF(
NOT(ISBLANK(CompDate)),
// 完成済み:完成日を表示
FORMAT(CompDate, "yy/mm/dd"),
// 未完成かつ納期が全体完成最大日以前:未完了と表示
IF(
NOT(ISBLANK(MaxCompDate)) && CurrentNoki <= MaxCompDate,
"未完了"
)
)
)

260512order8

別のメジャーの作成編集画面

[Power Pivot]-[管理]のデータビューでメジャーを作成するデータを表示し、下部のメジャーを作成する任意のセルを選択し、数式バーにメジャーを入力、編集する。
この画面でのメジャーは、「メジャー名:=数式」という書式で記述する。
Excelの数式バーと同様に右のアイコンと境界線のドラッグで、表示行を拡大縮小することができる。

8) ピボットテーブルの設定

ピボットテーブルのフィールドで、次のように項目を設定します。

最新

列 : Σ値。
行 : '品名'[品名]。
Σ値 : '注文'[注文数]、'完成'[完成数]、[M引当開始]、[M引当完了]、[M注残]。

推移

列 : '品名'[品名]、Σ値。
行 : 'カレンダ'[年月]、'カレンダ'[日付]。
Σ値 : '注文'[注文数]、'完成'[完成数]、[M注文累計]、[M完成累計]、[M注残]、[M遅延日数_納期]、[M引当完了_納期]。

5. サンプル

ダウンロード - 260512order.xlsx

6. 関連動画

入庫単価が変動する物の在庫金額と回転率 Excel Power Pivot : 当ブログ : 2026/05/15
https://bigarrow.tea-nifty.com/blog/2026/05/post-c320e1.html

Excelで在庫管理/生産管理(メジャーで期間末在庫を算出) : 当ブログ : 2025/06/17
https://bigarrow.tea-nifty.com/blog/2025/06/post-3f6a32.html

(Excel/Power BI)メジャーが予期しない値を返す原因とDAXコンテキストの理解 : 当ブログ : 2026/03/18
https://bigarrow.tea-nifty.com/blog/2025/03/post-8025c7.html

7. 余談

M遅延日数_納期とM遅延日数_納期は、Claudeとのやり取りでコードを得ました。
コードを読み解いたところ冗長な部分がり、手作業でシンプルにしました。
Claudeの回答をシンプルにすることをChatGPT、Copilot、Geminiに依頼しましたが、正解が得られなくなる回答しか得られませんでした。
この程度のDAX式になると、AI(LLM)で正解を得るのは難しいです。

Claudeは間違っていると伝えると、式を分解してデバックコードを送ってきます。デバックコードのやり取りを繰り返して、式の何処が悪いのかを探り、式を修正してきます。これは、人がデバックをする方法と同じです。その結果、式が冗長になりますが、正解に辿り着きました。

一方、ChatGPT、Copilot、Geminiは、単純に回答とその結果のエラーのやり取りの繰り返しで、正解に辿り着きませんでした。

DAX式が長いですが、ワークシート式やVBAで行っても似たようなロジックになると思われます。その場合、ワークシート式が一番可読性が悪い気がします。
VBAとDAXの可読性は、同程度ではないかと思われます。

ExcelやPower BIには、DAXのデバッグ機能が付いていません。なので、長いDAX式は、部分的に切り出したDAX式を作成して確認します。Tabular Editorのようなものが使えると嬉しいのですが。また、ExcelのDAXが古い、ExcelのDAXの公式仕様が無い、という欠点があります。

IATF16949では不良のみならず納期遅延も重要な要求事項に含まれるので、KPIで必ず確認し再発防止をしていることが必要です。

| | コメント (0)

より以前の記事一覧