1 なぜタイムインテリジェンスが重要か
ビジネスの世界では、数字を「時間軸」で比較することが日常的に行われている。
「今月の売上は先月より増えたのか?」
「今年の累計は去年の同時期と比べてどうか?」
「直近3ヶ月の平均はどう推移しているか?」
これらの問いに答えるには、単純な集計だけでは不十分である。「前年同期」「年初来累計」「移動平均」といった、時間に関連した複雑な計算が必要になる。
DAXには、こうした時間軸での計算を簡潔に記述するための専用関数群が用意されている。これをタイムインテリジェンス関数と呼ぶ。
タイムインテリジェンス関数の本質は、Filter Contextの中の「日付」を操作することである。第2回で学んだCALCULATEが「Filter Contextを変更する」関数だったことを思い出してほしい。タイムインテリジェンス関数は、CALCULATEと組み合わせて「日付のFilter Contextを特定のパターンで変更する」ための道具なのである。
今回使用するサンプルデータは、第1回・第2回と同じAdventureWorks Salesである。
2 日付テーブル:タイムインテリジェンスの土台
2.1 日付テーブルとは何か
タイムインテリジェンス関数を使う前に、まず「日付テーブル」について理解する必要がある。
通常、売上データには「注文日」や「出荷日」といった日付列が含まれている。しかし、この日付列をそのままタイムインテリジェンス関数に使うことには問題がある。
問題1:日付の欠損
売上テーブルには、売上があった日のデータしか存在しない。日曜定休の店舗であれば日曜日のレコードはなく、年末年始の休業期間もデータがない。この「穴」があると、「前月の売上」を計算しようとしたとき、正しい結果が得られないことがある。
問題2:属性の不足
「この日付は何年の第何四半期か」「何曜日か」「会計年度ではどの期に属するか」といった情報は、日付の値だけからは直接得られない。これらの属性がないと、「四半期ごとの集計」や「曜日別の分析」ができない。
問題3:粒度の制御
売上テーブルの日付列は、あくまで「トランザクションの属性」である。時間軸での分析を行うには、「時間」そのものを表すマスターテーブルが必要である。
これらの問題を解決するのが日付テーブル(Date Table、Calendar Table) である。
2.2 日付テーブルの要件
DAXのタイムインテリジェンス関数が正しく動作するには、日付テーブルが以下の要件を満たす必要がある。
要件1:連続した日付
分析対象期間のすべての日付が、1行ずつ存在すること。2023年1月1日から2023年12月31日までを分析するなら、その間の365日すべてが含まれていなければならない。土日祝日も、売上がゼロの日も、すべて含める。
要件2:重複なし
各日付は1回だけ出現すること。同じ日付が2行以上あってはならない。
要件3:年全体をカバー
タイムインテリジェンス関数の多くは「年」を単位として動作する。そのため、分析対象の各年について、1月1日から12月31日まで(または会計年度の開始日から終了日まで)の全日付が含まれている必要がある。
要件4:Date型の列
日付を格納する列は、文字列型ではなくDate型(またはDateTime型)でなければならない。
2.3 日付テーブルの構造
典型的な日付テーブルは、以下のような構造を持つ。
| 列名 | 説明 | 例 |
|---|---|---|
| Date | 日付(主キー) | 2023-04-15 |
| Year | 年 | 2023 |
| Month | 月(数値) | 4 |
| MonthName | 月名 | April |
| Quarter | 四半期 | Q2 |
| DayOfWeek | 曜日(数値) | 7 |
| DayName | 曜日名 | Saturday |
| IsWeekend | 週末フラグ | TRUE |
| FiscalYear | 会計年度 | FY2023 |
| FiscalQuarter | 会計四半期 | FQ1 |
これらの属性列は、集計やフィルタリングに活用される。
日本向け日付テーブル構造
| 列名 | 説明 | 例 |
|---|---|---|
| Date | 日付(主キー) | 2023-04-15 |
| Year | 年(西暦) | 2023 |
| Month | 月(数値) | 4 |
| MonthName | 月名(日本語) | 4月 |
| Quarter | 四半期(暦年) | Q2 |
| DayOfWeek | 曜日(数値、日曜=1) | 7 |
| DayName | 曜日名(日本語) | 土曜日 |
| IsWeekend | 週末フラグ | TRUE |
| FiscalYear | 会計年度 | FY2023 または 2023年度 |
| FiscalQuarter | 会計四半期 | FQ1 |
| FiscalYearQuarter | 会計年度+四半期 | FY2023-FQ1 |
| YearMonth | 年月(ソート用) | 202304 |
| YearMonthName | 年月(表示用) | 2023年4月 |
| Wareki | 和暦 | 令和5年 |
| IsHoliday | 祝日フラグ | FALSE |
| HolidayName | 祝日名 | (空白)または 昭和の日 |
| IsBusinessDay | 営業日フラグ | TRUE |
官公庁や一部の企業では和暦が必要な場合があります。
日本固有の祝日(振替休日含む)をマスタ管理する必要があります。祝日は法改正で変わることがあるため、年次でメンテナンスが必要です。
2.4 日付テーブルの作成方法
日付けテーブルが未作成の場合、以下の手順で作成します。
方法1:DAXで計算テーブルを作成(推奨)
ステップ1:ベーステーブルの作成
Power BI Desktopで「モデリング」→「新しいテーブル」を選択し、以下を入力:
Date = CALENDAR(DATE(2021, 1, 1), DATE(2025, 12, 31))
ステップ2:計算列の追加
作成したDateテーブルに、以下の計算列を順次追加する。
// 年(西暦)
Year = YEAR('Date'[Date])
// 月(数値)
Month = MONTH('Date'[Date])
// 月名(日本語)
MonthName = FORMAT('Date'[Date], "M月")
// 四半期(暦年)
Quarter = "Q" & QUARTER('Date'[Date])
// 曜日(数値:日曜=1)
DayOfWeek = WEEKDAY('Date'[Date], 1)
// 曜日名(日本語)
DayName = FORMAT('Date'[Date], "aaaa", "ja-JP" )
// 週末フラグ
IsWeekend = IF(WEEKDAY('Date'[Date], 2) >= 6, TRUE, FALSE)
// 会計年度(4月始まり)
FiscalYear =
IF(
MONTH('Date'[Date]) >= 4,
"FY" & YEAR('Date'[Date]),
"FY" & (YEAR('Date'[Date]) - 1)
)
// 会計四半期(4月始まり)
FiscalQuarter =
"FQ" &
SWITCH(
TRUE(),
MONTH('Date'[Date]) IN {4, 5, 6}, 1,
MONTH('Date'[Date]) IN {7, 8, 9}, 2,
MONTH('Date'[Date]) IN {10, 11, 12}, 3,
4
)
// 会計年度+四半期
FiscalYearQuarter = 'Date'[FiscalYear] & "-" & 'Date'[FiscalQuarter]
// 年月(ソート用)
YearMonth = YEAR('Date'[Date]) * 100 + MONTH('Date'[Date])
// 年月(表示用)
YearMonthName = FORMAT('Date'[Date], "YYYY年M月")
// 和暦
Wareki =
VAR Y = YEAR('Date'[Date])
VAR M = MONTH('Date'[Date])
VAR D = DAY('Date'[Date])
VAR DateNum = Y * 10000 + M * 100 + D
RETURN
SWITCH(
TRUE(),
DateNum >= 20190501, "令和" & (Y - 2018) & "年",
DateNum >= 19890108, "平成" & (Y - 1988) & "年",
DateNum >= 19261225, "昭和" & (Y - 1925) & "年",
"対象外"
)
ステップ3:日付テーブルとしてマーク
- Dateテーブルを選択
- 「テーブルツール」→「日付テーブルとしてマーク」
- Date列を日付列として指定
方法2:一括定義(計算テーブル)
すべての列を一度に定義する方法:
Date =
VAR BaseCalendar = CALENDAR(DATE(2021, 1, 1), DATE(2025, 12, 31))
RETURN
ADDCOLUMNS(
BaseCalendar,
"Year", YEAR([Date]),
"Month", MONTH([Date]),
"MonthName", FORMAT([Date], "M月"),
"Quarter", "Q" & QUARTER([Date]),
"DayOfWeek", WEEKDAY([Date], 1),
"DayName", FORMAT([Date], "aaaa", "ja-JP" ),
"IsWeekend", IF(WEEKDAY([Date], 2) >= 6, TRUE, FALSE),
"FiscalYear", IF(MONTH([Date]) >= 4, "FY" & YEAR([Date]), "FY" & (YEAR([Date]) - 1)),
"FiscalQuarter", "FQ" & SWITCH(TRUE(), MONTH([Date]) IN {4,5,6}, 1, MONTH([Date]) IN {7,8,9}, 2, MONTH([Date]) IN {10,11,12}, 3, 4),
"YearMonth", YEAR([Date]) * 100 + MONTH([Date]),
"YearMonthName", FORMAT([Date], "YYYY年M月")
)
祝日の追加
日本の祝日は、移動祝日(成人の日など)、振替休日、法改正による変更があり、ルールベースでの自動生成は困難である。
推奨方法:内閣府の公式データを使用
内閣府が公開している祝日データ( https://www8.cao.go.jp/chosei/shukujitsu/gaiyou.html )を年1回(1月頃)にデータを更新する。法改正や特例(オリンピック等)があれば随時対応する。政府公式データのため正確で、振替休日・国民の休日も含まれている。
年末年始やお盆休みなど企業独自の休業日は、祝日テーブルとは別に「会社カレンダー」テーブルで管理する。
リレーションシップの設定
日付テーブルとファクトテーブル(Salesなど)を接続:
- モデルビューを開く
- Date[Date] から Sales[OrderDate] へドラッグ
- リレーションシップが作成される(多対一)
注意点
| 項目 | 推奨事項 |
|---|---|
| 期間の範囲 | 分析対象期間 + 前後数年の余裕を持たせる |
| 年全体をカバー | 各年の1月1日〜12月31日を含める |
| 重複なし | 各日付は1行のみ |
| データ型 | Date列は必ずDate型 |
| 日付テーブルとしてマーク | タイムインテリジェンス関数の正常動作に必須 |
会計年度が4月始まりであっても、日付テーブルは1月1日から12月31日の暦年ベースで作成する。
3 累計の計算:TOTALxTD関数群
3.1 累計とは何か
ビジネスレポートでは「累計」が頻繁に登場する。累計とは、ある基準日から現在の日付までの合計値のことである。
最もよく使われるのは年初来累計(Year-To-Date、YTD) である。これは、その年の1月1日から現在の日付までの売上合計を意味する。
例えば、2023年4月の売上レポートを見ているとき
- 「4月の売上」:4月1日〜4月30日の売上
- 「年初来累計」:1月1日〜4月30日の売上
累計は、年間目標に対する進捗を把握したり、前年同期と比較したりするのに不可欠な指標である。
3.2 TOTALYTD:年初来累計
DAXでは、TOTALYTD関数を使って年初来累計を簡単に計算できる。
構文
TOTALYTD(
<計算する式>,
<日付列>,
[<追加のフィルタ>],
[<年度終了日>]
)
基本例
DEFINE
MEASURE Sales[Sales Amount] =
SUMX (
Sales,
Sales[Quantity] * Sales[Net Price]
)
MEASURE Sales[YTD Sales] =
TOTALYTD (
[Sales Amount],
'Date'[Date]
)
EVALUATE
SUMMARIZECOLUMNS (
'Date'[Year],
'Date'[Month Number],
"Sales", FORMAT([Sales Amount], "$#,#0"),
"YTD Sales", FORMAT([YTD Sales], "$#,#0")
)
ORDER BY 'Date'[Year], 'Date'[Month Number]
このクエリを実行すると、各月の売上と、その月時点での年初来累計が表示される。
3.3 TOTALYTDの動作原理
TOTALYTDは内部で何をしているのだろうか。実は、この関数は以下のCALCULATE式と等価である。
MEASURE Sales[YTD Sales Manual] =
CALCULATE (
[Sales Amount],
DATESYTD('Date'[Date])
)
DATESYTD関数は、現在のFilter Contextにある日付を見て、「その年の1月1日から、Filter Context内の最後の日付まで」の日付セットを返す。
つまり、Filter Contextが「2023年4月」のとき
- DATESYTDは「2023年1月1日〜2023年4月30日」の日付セットを返す
- CALCULATEがこの日付セットでFilter Contextを置き換える
- [Sales Amount]がこの新しいFilter Contextで評価される
- 結果として、1月から4月までの売上合計が返る
TOTALYTDは、この一連の処理を1つの関数にまとめたものである。このように、複雑な処理を簡潔に書けるようにした関数を糖衣構文(シンタックスシュガー) と呼ぶ。
3.4 年が変わるとリセットされる
累計の重要な特性は、「年が変わるとリセットされる」ことである。
2022年1月になると、YTD Salesは2022年1月の売上だけになる。これは、DATESYTDが「その年の1月1日から」という範囲を返すためである。
3.5 TOTALMTD と TOTALQTD
年初来だけでなく、月初来と四半期初来の累計も同様に計算できる。
TOTALMTD(Month-To-Date):月初来累計
MEASURE Sales[MTD Sales] =
TOTALMTD (
[Sales Amount],
'Date'[Date]
)
日単位でレポートを見るとき、「今月これまでの売上」を表示するのに使う。
TOTALQTD(Quarter-To-Date):四半期初来累計
MEASURE Sales[QTD Sales] =
TOTALQTD (
[Sales Amount],
'Date'[Date]
)
四半期ごとに業績を管理している企業で、四半期目標に対する進捗を把握するのに使う。
3.6 3つの累計を並べて確認
DEFINE
MEASURE Sales[Sales Amount] =
SUMX (
Sales,
Sales[Quantity] * Sales[Net Price]
)
MEASURE Sales[MTD Sales] =
TOTALMTD (
[Sales Amount],
'Date'[Date]
)
MEASURE Sales[QTD Sales] =
TOTALQTD (
[Sales Amount],
'Date'[Date]
)
MEASURE Sales[YTD Sales] =
TOTALYTD (
[Sales Amount],
'Date'[Date]
)
EVALUATE
SUMMARIZECOLUMNS (
'Date'[Year],
'Date'[Month Number],
"Sales", FORMAT([Sales Amount], "$#,#0"),
"MTD", FORMAT([MTD Sales], "$#,#0"),
"QTD", FORMAT([QTD Sales], "$#,#0"),
"YTD", FORMAT([YTD Sales], "$#,#0")
)
ORDER BY 'Date'[Year], 'Date'[Month Number]
月単位でグループ化しているため、MTDとSalesは同じ値になる(月全体がFilter Contextに含まれているため)。日単位でグループ化すれば、MTDの累積効果が見える。
4 前年同期比較:時間を遡る
4.1 前年比較の重要性
ビジネスでは「前年同期比」が最も重視される比較指標の一つである。
なぜ「前月比」ではなく「前年同期比」なのか。それは、多くのビジネスに季節性があるからである。小売業であれば12月は繁忙期、2月は閑散期といった具合に、月によって売上水準が大きく異なる。
2月の売上を1月と比較しても、季節性の影響で正しい評価ができない。しかし、去年の2月と比較すれば、同じ季節条件での成長を測ることができる。
4.2 SAMEPERIODLASTYEAR:前年同期
SAMEPERIODLASTYEARは、現在のFilter Contextにある日付を「1年前」にシフトした日付セットを返す関数である。
DEFINE
MEASURE Sales[Sales Amount] =
SUMX (
Sales,
Sales[Quantity] * Sales[Net Price]
)
MEASURE Sales[PY Sales] =
CALCULATE (
[Sales Amount],
SAMEPERIODLASTYEAR('Date'[Date])
)
EVALUATE
SUMMARIZECOLUMNS (
'Date'[Year],
'Date'[Month Number],
"Sales", FORMAT([Sales Amount], "$#,#0"),
"PY Sales", FORMAT([PY Sales], "$#,#0")
)
ORDER BY 'Date'[Year], 'Date'[Month Number]
4.3 SAMEPERIODLASTYEARはDATEADDのシンタックスシュガー
SAMEPERIODLASTYEARは、実は別の関数の省略形である。以下の2つは完全に同じ動作をする
SAMEPERIODLASTYEAR('Date'[Date])
DATEADD('Date'[Date], -1, YEAR)
SAMEPERIODLASTYEARは、DATEADDを「-1年」という固定パラメータで呼び出すシンタックスシュガーなのである。コードの意図が明確になるため、前年比較にはSAMEPERIODLASTYEARを使うことが推奨される。
4.4 前年比(YoY Growth)の計算
前年同期の値が取得できれば、成長率を計算できる。
DEFINE
MEASURE Sales[Sales Amount] =
SUMX (
Sales,
Sales[Quantity] * Sales[Net Price]
)
MEASURE Sales[PY Sales] =
CALCULATE (
[Sales Amount],
SAMEPERIODLASTYEAR('Date'[Date])
)
MEASURE Sales[YoY Growth] =
VAR CurrentSales = [Sales Amount]
VAR PreviousSales = [PY Sales]
RETURN
DIVIDE (
CurrentSales - PreviousSales,
PreviousSales
)
EVALUATE
SUMMARIZECOLUMNS (
'Date'[Year],
'Date'[Month Number],
"Sales", FORMAT([Sales Amount], "$#,#0"),
"PY Sales", FORMAT([PY Sales], "$#,#0"),
"YoY Growth", FORMAT([YoY Growth], "+#0.0%;-#0.0%")
)
ORDER BY 'Date'[Year], 'Date'[Month Number]
成長率の計算式は「(今年 - 前年) / 前年」である。DIVIDE関数を使うことで、前年がゼロの場合のエラーを回避している。
4.5 前年累計比較(YTD YoY)
「今年の年初来累計」と「去年の同時期の年初来累計」を比較することも一般的である。
例えば、2023年4月時点で
- 今年のYTD:2023年1月〜4月の累計
- 去年のYTD:2022年1月〜4月の累計
この比較により、「同じ時期までの進捗」を比べることができる。
DEFINE
MEASURE Sales[Sales Amount] =
SUMX (
Sales,
Sales[Quantity] * Sales[Net Price]
)
MEASURE Sales[YTD Sales] =
TOTALYTD (
[Sales Amount],
'Date'[Date]
)
MEASURE Sales[PY YTD Sales] =
CALCULATE (
[YTD Sales],
SAMEPERIODLASTYEAR('Date'[Date])
)
MEASURE Sales[YTD YoY Growth] =
DIVIDE (
[YTD Sales] - [PY YTD Sales],
[PY YTD Sales]
)
EVALUATE
SUMMARIZECOLUMNS (
'Date'[Year],
'Date'[Month Number],
"YTD Sales", FORMAT([YTD Sales], "$#,#0"),
"PY YTD Sales", FORMAT([PY YTD Sales], "$#,#0"),
"YTD YoY", FORMAT([YTD YoY Growth], "+#0.0%;-#0.0%")
)
ORDER BY 'Date'[Year], 'Date'[Month Number]
ここでのポイントは、SAMEPERIODLASTYEARを[YTD Sales]メジャーに対して適用していることである。これにより、「去年の同じ月時点でのYTD」が計算される。
5 DATEADD:汎用的な期間シフト
5.1 DATEADDの構文
DATEADDは、タイムインテリジェンスの中核となる汎用関数である。
DATEADD(
<日付列>,
<移動数>,
<期間の種類>
)
期間の種類
- DAY:日単位でシフト
- MONTH:月単位でシフト
- QUARTER:四半期単位でシフト
- YEAR:年単位でシフト
移動数
- 負の数:過去へシフト
- 正の数:未来へシフト
5.2 DATEADDの動作アルゴリズム(重要)
DATEADDの動作は、期間の種類によって異なるアルゴリズムを使用する。これを理解することが、タイムインテリジェンスを正しく使いこなす鍵となる。
DAYの場合
すべての日付を指定日数分シフトする。月末の扱いでは、シフト先の月に該当日が存在しない場合、その月の最終日が返される。
MONTH、QUARTER、YEARの場合
DATEADDは、入力された日付を「月単位」で分析する。
- 月全体が選択されている場合:その月全体をシフトする
- 月の一部だけが選択されている場合:各日付を個別にシフトする
この区別が重要である。例えば、2月(28日)全体が選択されているとき、DATEADDで1ヶ月前にシフトすると、1月全体(31日)が返される。月の日数の違いを自動的に考慮してくれるのである。
-- 2月全体(28日)から1ヶ月シフト → 1月全体(31日)が返る
DATEADD('Date'[Date], -1, MONTH)
一方、2月の一部(例:2月1日〜2月15日)だけが選択されている場合は、各日付が個別に1ヶ月シフトされ、1月1日〜1月15日が返される。
5.3 DATEADDの使用例
前年同期(SAMEPERIODLASTYEARと等価)
MEASURE Sales[PY Sales] =
CALCULATE (
[Sales Amount],
DATEADD('Date'[Date], -1, YEAR)
)
前月
MEASURE Sales[PM Sales] =
CALCULATE (
[Sales Amount],
DATEADD('Date'[Date], -1, MONTH)
)
前四半期
MEASURE Sales[PQ Sales] =
CALCULATE (
[Sales Amount],
DATEADD('Date'[Date], -1, QUARTER)
)
2年前
MEASURE Sales[2Y Ago Sales] =
CALCULATE (
[Sales Amount],
DATEADD('Date'[Date], -2, YEAR)
)
5.4 前月比較(MoM)の実装
DEFINE
MEASURE Sales[Sales Amount] =
SUMX (
Sales,
Sales[Quantity] * Sales[Net Price]
)
MEASURE Sales[PM Sales] =
CALCULATE (
[Sales Amount],
DATEADD('Date'[Date], -1, MONTH)
)
MEASURE Sales[MoM Growth] =
DIVIDE (
[Sales Amount] - [PM Sales],
[PM Sales]
)
EVALUATE
SUMMARIZECOLUMNS (
'Date'[Year],
'Date'[Month Number],
"Sales", FORMAT([Sales Amount], "$#,#0"),
"PM Sales", FORMAT([PM Sales], "$#,#0"),
"MoM Growth", FORMAT([MoM Growth], "+#0.0%;-#0.0%")
)
ORDER BY 'Date'[Year], 'Date'[Month Number]
前月比は季節性の影響を受けるため、YoYほど一般的ではないが、短期的なトレンドを把握するのに有用である。
5.5 DATEADDの誤解
多くの人がDATEADDを「日付に年月日を足し引きする関数」と誤解している。SQLやExcelのDATEADD関数がそのように動作するためである。
SQLやExcelのDATEADD
単一の日付値を受け取り、単一の日付値を返す。
-- SQL Server
DATEADD(month, -1, '2023-04-15') -- 結果: 2023-03-15
-- Excel
=EDATE(A1, -1) -- 1ヶ月前の日付
DAXのDATEADD
DAXのDATEADDは全く異なる。テーブル(日付の列) を受け取り、テーブル(日付のセット) を返す。
| 項目 | SQL/ExcelのDATEADD | DAXのDATEADD |
|---|---|---|
| 入力 | 単一の日付値 | 日付列(テーブル) |
| 出力 | 単一の日付値 | 日付のセット(テーブル) |
| 用途 | 日付の計算 | Filter Contextの操作 |
| 使い方 | 単独で使用 | CALCULATEと組み合わせ |
単純な日付計算をしたい場合
DAXで単純な日付計算(1年前の日付値を取得)をするには、DATE関数を使う。
-- 単一の日付値から1年前を計算
VAR OneYearAgo = DATE(YEAR([Date]) - 1, MONTH([Date]), DAY([Date]))
または
-- 計算列で使用
Previous Year Date = EDATE('Date'[Date], -12)
6 PARALLELPERIOD:完全な期間を返す
6.1 PARLLELPERIODの動作原理
PARALLELPERIODは、DATEADDとは異なるアルゴリズムで動作する。
PARLLELPERIODは、入力された日付に「少なくとも1日でも含まれている期間」を検出し、その期間全体をシフトして返す。
例えば、入力が「2019年2月1日〜3月10日」の場合
- MONTH指定:2月と3月に日付が含まれているので、シフト先も2ヶ月分が返る
- QUARTER指定:Q1に日付が含まれているので、シフト先も四半期全体が返る
- YEAR指定:2019年に日付が含まれているので、シフト先も年全体が返る
重要 PARALLELPERIODはDAY指定をサポートしていない。
6.2 DATEADDとPARALLELPERIODの違い
両者の違いは、「年レベル」で見ると同じに見えるが、「月レベル」で見ると明確になる。
DEFINE
MEASURE Sales[Sales Amount] =
SUMX (
Sales,
Sales[Quantity] * Sales[Net Price]
)
MEASURE Sales[DATEADD -1Y] =
CALCULATE (
[Sales Amount],
DATEADD('Date'[Date], -1, YEAR)
)
MEASURE Sales[PARALLELPERIOD -1Y] =
CALCULATE (
[Sales Amount],
PARALLELPERIOD('Date'[Date], -1, YEAR)
)
EVALUATE
SUMMARIZECOLUMNS (
'Date'[Year],
'Date'[Month Number],
"Sales", FORMAT([Sales Amount], "$#,#0"),
"DATEADD", FORMAT([DATEADD -1Y], "$#,#0"),
"PARALLELPERIOD", FORMAT([PARALLELPERIOD -1Y], "$#,#0")
)
ORDER BY 'Date'[Year], 'Date'[Month Number]
結果の違い
- DATEADD:各月に対して「前年の同じ月」を返す(2022年5月 → 2021年5月)
- PARALLELPERIOD:「前年全体」を返す(2022年のどの月でも → 2021年全体)
この違いは重要である。DATEADDは「同じ粒度で比較」、PARALLELPERIODは「完全な期間と比較」という異なる用途を持つ。
6.3 使い分けの指針
DATEADDを使う場面
- 前年同月、前月、前四半期など「同じ粒度」での比較
- 日付の範囲をそのままシフトしたい場合
- SAMEPERIODLASTYEARの代わり(より柔軟なパラメータ指定が必要な場合)
PARALLELPERIODを使う場面
- 「前年全体」「前四半期全体」など完全な期間が必要な場合
- 入力の粒度に関係なく、特定の期間全体を取得したい場合
なお、以下の様に記述すれば、先のPARALLELPERIODを使って同じ結果を出すことが出来る。
MEASURE Sales[PARALLELPERIOD -1Y] =
CALCULATE (
[Sales Amount],
PARALLELPERIOD('Date'[Date], -12, MONTH)
)
ただし、日単位でフィルタされている場合は結果が異なる。動作の意味を理解することが重要。
7 PREVIOUS系・NEXT系関数
7.1 PREVIOUS系関数の構造
DAXには、「前の期間」を取得する専用関数がある。
- PREVIOUSDAY:前日
- PREVIOUSMONTH:前月
- PREVIOUSQUARTER:前四半期
- PREVIOUSYEAR:前年
これらの関数は、内部的にPARALLELPERIODとFIRSTDATEを組み合わせて実装されている。
-- 以下は等価
PREVIOUSMONTH('Date'[Date])
PARALLELPERIOD(FIRSTDATE('Date'[Date]), -1, MONTH)
PREVIOUSYEAR('Date'[Date])
PARALLELPERIOD(FIRSTDATE('Date'[Date]), -1, YEAR)
FIRSTDATEが使われるため、入力に複数の期間が含まれていても、最初の日付を基準にした単一の期間のみが返される。これがPARALLELPERIODとの重要な違いである。
7.2 PREVIOUSYEARとPARALLELPERIODの違い
この違いは、特に「合計行」で顕著になる。
DEFINE
MEASURE Sales[Sales Amount] =
SUMX (
Sales,
Sales[Quantity] * Sales[Net Price]
)
MEASURE Sales[PREVIOUSYEAR] =
CALCULATE (
[Sales Amount],
PREVIOUSYEAR('Date'[Date])
)
MEASURE Sales[PARALLELPERIOD -1Y] =
CALCULATE (
[Sales Amount],
PARALLELPERIOD('Date'[Date], -1, YEAR)
)
VAR DetailRows =
SUMMARIZECOLUMNS (
'Date'[Year],
"Sales", [Sales Amount],
"PREVIOUSYEAR", [PREVIOUSYEAR],
"PARALLELPERIOD", [PARALLELPERIOD -1Y]
)
VAR TotalRow =
ROW (
"Year", "Total",
"Sales", CALCULATE([Sales Amount], ALL('Date')),
"PREVIOUSYEAR", CALCULATE([PREVIOUSYEAR], ALL('Date')),
"PARALLELPERIOD", CALCULATE([PARALLELPERIOD -1Y], ALL('Date'))
)
EVALUATE
UNION(DetailRows, TotalRow)
ORDER BY [Year]
合計行での動作の違い
合計行では、Filter Contextに複数年が含まれる。
- PREVIOUSYEAR:最初の年(例:2007年)の前年を探すが、2006年がDateテーブルに存在しないためBLANKを返す
- PARALLELPERIOD:すべての年を1年シフトし、存在する範囲(2007〜2008年など)の合計を返す
この違いを理解していないと、レポートの合計行で予期しない結果になることがある。
7.3 NEXT系関数
PREVIOUS系の逆に、「次の期間」を返す関数もある。
- NEXTDAY:翌日
- NEXTMONTH:翌月
- NEXTQUARTER:翌四半期
- NEXTYEAR:翌年
これらは内部的にLASTDATEと正の数を使用する。
-- 以下は等価
NEXTMONTH('Date'[Date])
PARALLELPERIOD(LASTDATE('Date'[Date]), 1, MONTH)
予算や予測との比較、または過去データ分析時に「その時点での翌期実績」を見る場合に使う。
8 日付範囲を返す関数
8.1 DATESBETWEEN:絶対的な日付範囲
DATESBETWEENは、指定した開始日から終了日までの日付セットを返す。
DEFINE
MEASURE Sales[Sales Amount] =
SUMX (
Sales,
Sales[Quantity] * Sales[Net Price]
)
-- 2008年第1四半期の売上(固定期間)
MEASURE Sales[Q1 2024 Sales] =
CALCULATE (
[Sales Amount],
DATESBETWEEN (
'Date'[Date],
DATE(2024, 1, 1),
DATE(2024, 3, 31)
)
)
EVALUATE
SUMMARIZECOLUMNS (
'Date'[Year],
"Total Sales", FORMAT([Sales Amount], "$#,#0"),
"Q1 2024", FORMAT([Q1 2024 Sales], "$#,#0")
)
ORDER BY 'Date'[Year]
DATESBETWEENは「絶対的な」日付範囲を指定する。どのFilter Contextでも、常に同じ期間を返す。
8.2 DATESINPERIOD:相対的な日付範囲
DATESINPERIODは、基準日から相対的な期間を指定する。
DATESINPERIOD(
<日付列>,
<基準日>,
<期間数>,
<期間の種類>
)
例:直近3ヶ月
DEFINE
MEASURE Sales[Sales Amount] =
SUMX (
Sales,
Sales[Quantity] * Sales[Net Price]
)
MEASURE Sales[Last 3 Months] =
CALCULATE (
[Sales Amount],
DATESINPERIOD (
'Date'[Date],
MAX('Date'[Date]), -- Filter Context内の最後の日付
-3,
MONTH
)
)
EVALUATE
SUMMARIZECOLUMNS (
'Date'[Year],
'Date'[Month Number],
"Sales", FORMAT([Sales Amount], "$#,#0"),
"Last 3 Months", FORMAT([Last 3 Months], "$#,#0")
)
ORDER BY 'Date'[Year], 'Date'[Month Number]
この例では、MAX('Date'[Date])を基準日としている。これにより、Filter Context内の最後の日付から過去3ヶ月間の日付セットが返される。
DATESINPERIODは移動平均の計算に特に有用である。
9 移動平均の計算
9.1 移動平均とは
移動平均は、データの短期的な変動を平滑化し、トレンドを把握するための手法である。
例えば「3ヶ月移動平均」は、直近3ヶ月の平均値を計算する。毎月計算すると、この「窓」が1ヶ月ずつスライドしていく。
| 月 | 売上 | 3ヶ月移動平均 |
|---|---|---|
| 1月 | 100 | - |
| 2月 | 120 | - |
| 3月 | 110 | 110 (100+120+110)/3 |
| 4月 | 130 | 120 (120+110+130)/3 |
| 5月 | 90 | 110 (110+130+90)/3 |
9.2 DATESINPERIODを使った移動平均
DEFINE
-- 売上金額:数量 × 単価 の合計
MEASURE Sales[Sales Amount] =
SUMX (
Sales,
Sales[Quantity] * Sales[Net Price]
)
-- 3ヶ月移動平均
MEASURE Sales[3M Moving Avg] =
VAR TotalSales =
CALCULATE (
[Sales Amount],
-- 現在の最終日から過去3ヶ月間の日付セットでフィルタ
DATESINPERIOD (
'Date'[Date],
MAX('Date'[Date]),
-3,
MONTH
)
)
RETURN
DIVIDE(TotalSales, 3)
EVALUATE
SUMMARIZECOLUMNS (
'Date'[Year],
'Date'[Month Number],
"Sales", FORMAT([Sales Amount], "$#,##0"),
"3M Avg", FORMAT([3M Moving Avg], "$#,##0")
)
ORDER BY 'Date'[Year], 'Date'[Month Number]
9.3 より正確な移動平均(月数を動的に計算)
上記の実装は「常に3で割る」が、データの最初の2ヶ月では3ヶ月分のデータがない。より正確な実装は、実際に存在する月数で割る。
DEFINE
-- 売上金額:数量 × 単価 の合計
MEASURE Sales[Sales Amount] =
SUMX (
Sales,
Sales[Quantity] * Sales[Net Price]
)
-- 3ヶ月移動平均(実際の月数で割る正確版)
MEASURE Sales[3M Moving Avg Correct] =
VAR Period =
DATESINPERIOD (
'Date'[Date],
MAX('Date'[Date]),
-3,
MONTH
)
-- 期間内の実際の月数をカウント(データ開始時に3ヶ月未満の場合に対応)
VAR MonthsInPeriod =
CALCULATE (
DISTINCTCOUNT('Date'[Month Number]),
Period
)
-- 期間内の売上合計
VAR TotalSales =
CALCULATE (
[Sales Amount],
Period
)
RETURN
-- 実際の月数で割る(データ不足時も正確な平均を算出)
DIVIDE(TotalSales, MonthsInPeriod)
EVALUATE
SUMMARIZECOLUMNS (
'Date'[Year],
'Date'[Month Number],
"Sales", FORMAT([Sales Amount], "$#,##0"),
"3M Avg", FORMAT([3M Moving Avg Correct], "$#,##0")
)
ORDER BY 'Date'[Year], 'Date'[Month Number]
この実装では、期間内の売上合計を実際の月数で割っている。
10 半加算メジャー(Semi-Additive Measures)
10.1 半加算メジャーとは
ここまで扱ってきた「売上」は、単純に足し算できる加算メジャー(Additive Measure) である。1月の売上と2月の売上を足せば、1〜2月の売上になる。
しかし、すべての指標がこのように足し算できるわけではない。
例1:在庫数
1月末の在庫100個と2月末の在庫120個を足しても「1〜2月の在庫」にはならない。在庫は「ある時点での状態」を表す値であり、期間を通じた累積ではない。
例2:為替レート
1月のレート150円と2月のレート148円を足しても意味がない。期末時点のレートを使うべきである。
例3:累計ユニーク顧客数
1月に100人、2月に120人の顧客がいたとき、単純に足して220人とはならない。同じ顧客が両月で購入していれば重複するためである。
これらは半加算メジャー(Semi-Additive Measure) と呼ばれる。
10.2 半加算メジャーの特性
半加算メジャーには以下の特性がある:
| 集計軸 | 加算可否 | 例 |
|---|---|---|
| 時間軸 | × 加算できない | 1月の在庫 + 2月の在庫 ≠ 1〜2月の在庫 |
| 他の軸 | ○ 加算できる | A店舗の在庫 + B店舗の在庫 = 全店舗の在庫 |
そのため、時間軸での集計には特別な処理が必要である。
10.3 為替レートで理解する半加算メジャー
サンプルデータのSalesテーブルには為替レート(Exchange Rate)列がある。これを使って半加算メジャーの概念を確認する。
DEFINE
-- 売上金額(加算メジャー:足し算できる)
MEASURE Sales[Sales Amount] =
SUMX (
Sales,
Sales[Quantity] * Sales[Net Price]
)
-- 為替レートの単純平均(参考用)
MEASURE Sales[Exchange Rate AVG] =
AVERAGE(Sales[Exchange Rate])
-- 期末の為替レート(半加算メジャー:期末値を使用)
MEASURE Sales[Exchange Rate End] =
CALCULATE (
AVERAGE(Sales[Exchange Rate]),
LASTDATE('Date'[Date])
)
EVALUATE
SUMMARIZECOLUMNS (
'Date'[Year],
'Date'[Month Number],
"Sales", FORMAT([Sales Amount], "$#,##0"),
"Rate AVG", FORMAT([Exchange Rate AVG], "0.000"),
"Rate End", FORMAT([Exchange Rate End], "0.000")
)
ORDER BY 'Date'[Year], 'Date'[Month Number]
Rate AVG は月内の平均レート、Rate End は月末時点のレートを返す。為替換算では通常、期末レートを使うため Rate End が適切である。
10.4 LASTDATE:期間の最終日の値
LASTDATE は、Filter Context内の最後の日付だけを返すテーブル関数である。
-- LASTDATEの動作イメージ
-- Filter Contextが「2023年4月」の場合
LASTDATE('Date'[Date]) -- → 2023年4月30日のみを返す
これをCALCULATEと組み合わせることで、「期間の最終日の値」を取得できる。
10.5 累計ユニーク顧客数:足し算できない例
半加算メジャーのもう一つの典型例として、累計ユニーク顧客数がある。
DEFINE
-- 当月の顧客数
MEASURE Sales[Monthly Customers] =
DISTINCTCOUNT(Sales[CustomerKey])
-- 年初来の累計ユニーク顧客数
MEASURE Sales[YTD Unique Customers] =
CALCULATE (
DISTINCTCOUNT(Sales[CustomerKey]),
DATESYTD('Date'[Date])
)
EVALUATE
SUMMARIZECOLUMNS (
'Date'[Year],
'Date'[Month Number],
"Monthly Customers", [Monthly Customers],
"YTD Unique Customers", [YTD Unique Customers]
)
ORDER BY 'Date'[Year], 'Date'[Month Number]
注目ポイント:
- Monthly Customers の累計 ≠ YTD Unique Customers
- 同じ顧客が複数月で購入しているため、単純な足し算より少なくなる
- これが半加算メジャーの特性である
10.6 LASTNONBLANK:データがある最後の日
データが毎日記録されているとは限らない。週次や月次でしか記録されていない場合、LASTDATEではデータのない日を参照してしまう可能性がある。
LASTNONBLANK は、指定した式が値を返す最後の日付を見つける。
DEFINE
MEASURE Sales[Sales Amount] =
SUMX (
Sales,
Sales[Quantity] * Sales[Net Price]
)
-- データが存在する最終日の売上
MEASURE Sales[Last Day Sales] =
CALCULATE (
[Sales Amount],
LASTNONBLANK(
'Date'[Date],
CALCULATE([Sales Amount])
)
)
EVALUATE
SUMMARIZECOLUMNS (
'Date'[Year],
'Date'[Month Number],
"Sales", FORMAT([Sales Amount], "$#,##0"),
"Last Day Sales", FORMAT([Last Day Sales], "$#,##0")
)
ORDER BY 'Date'[Year], 'Date'[Month Number]
LASTDATEとLASTNONBLANKの違い:
| 関数 | 動作 | 使用場面 |
|---|---|---|
| LASTDATE | Filter Context内の最終日を返す | データが毎日存在する場合 |
| LASTNONBLANK | データがある最終日を返す | データに欠損がある場合 |
10.7 まとめ:半加算メジャーの判断基準
メジャーを設計する際、以下の質問で半加算メジャーかどうかを判断できる:
「1月の値と2月の値を足したものは、1〜2月の値として意味があるか?」
| 回答 | メジャーの種類 | 例 | 時間軸の集計 |
|---|---|---|---|
| はい | 加算メジャー | 売上、数量、コスト | SUM、TOTALYTD |
| いいえ | 半加算メジャー | 在庫、残高、為替レート、ユニーク数 | LASTDATE、LASTNONBLANK、DISTINCTCOUNT |
半加算メジャーの扱いは複雑なため、要件に応じて適切な集計方法を選択する必要がある。
11 会計年度への対応
11.1 会計年度とは
多くの企業では、「会計年度」が暦年(1月〜12月)と異なる。
| 地域・業種 | 会計年度 | 年度終了日 |
|---|---|---|
| 日本の多くの企業 | 4月始まり | 3月31日 |
| 米国の連邦政府 | 10月始まり | 9月30日 |
| 小売業の一部 | 2月始まり | 1月31日 |
タイムインテリジェンス関数は、デフォルトでは暦年(1月〜12月)を基準にしている。日本企業で使う場合、会計年度への対応が必要になる。
11.2 TOTALYTDでの会計年度設定
TOTALYTDには、第4引数で「年度終了日」を指定できる。
TOTALYTD(
<式>,
<日付列>,
[<フィルタ>],
[<年度終了日>] -- "M/DD" または "MM/DD" 形式
)
4月始まり(3月31日終了)の場合:
DEFINE
-- 売上金額
MEASURE Sales[Sales Amount] =
SUMX (
Sales,
Sales[Quantity] * Sales[Net Price]
)
-- 暦年ベースの年初来累計(1月リセット)
MEASURE Sales[Calendar YTD] =
TOTALYTD (
[Sales Amount],
'Date'[Date]
)
-- 会計年度ベースの年初来累計(4月リセット)
-- DATESYTDの第2引数で年度終了日を指定
MEASURE Sales[Fiscal YTD] =
CALCULATE (
[Sales Amount],
DATESYTD('Date'[Date], "3/31")
)
EVALUATE
SUMMARIZECOLUMNS (
'Date'[Year],
'Date'[Month Number],
"Sales", FORMAT([Sales Amount], "$#,##0"),
"Calendar YTD", FORMAT([Calendar YTD], "$#,##0"),
"Fiscal YTD", FORMAT([Fiscal YTD], "$#,##0")
)
ORDER BY 'Date'[Year], 'Date'[Month Number]
11.3 会計年度での累計の動き
暦年YTDと会計年度YTDの違い:
| Year | Month | Sales | Calendar YTD | Fiscal YTD | 備考 |
|---|---|---|---|---|---|
| 2022 | 1 | $1,000 | $1,000 | $10,000 | Calendar YTDリセット |
| 2022 | 2 | $1,200 | $2,200 | $11,200 | |
| 2022 | 3 | $1,100 | $3,300 | $12,300 | |
| 2022 | 4 | $1,300 | $4,600 | $1,300 | Fiscal YTDリセット |
| 2022 | 5 | $900 | $5,500 | $2,200 |
ポイント:
- Calendar YTD:1月にリセット(暦年ベース)
- Fiscal YTD:4月にリセット(会計年度ベース、3月末決算)
11.4 会計年度用の計算列
より柔軟な会計年度対応には、日付テーブルに会計年度関連の列を追加することを推奨する。これにより、SUMMARIZECOLUMNSで会計年度単位のグループ化が可能になる。
Power BIで以下の計算列を作成:
-- 会計年度(4月始まり)
-- 4月〜12月 → 当年、1月〜3月 → 前年
Fiscal Year =
IF(
MONTH('Date'[Date]) >= 4,
"FY" & YEAR('Date'[Date]),
"FY" & (YEAR('Date'[Date]) - 1)
)
-- 会計四半期(4月始まり)
-- 4-6月:FQ1、7-9月:FQ2、10-12月:FQ3、1-3月:FQ4
Fiscal Quarter =
"FQ" &
SWITCH(
TRUE(),
MONTH('Date'[Date]) IN {4, 5, 6}, 1,
MONTH('Date'[Date]) IN {7, 8, 9}, 2,
MONTH('Date'[Date]) IN {10, 11, 12}, 3,
4
)
-- 会計年度+四半期(表示用)
Fiscal Year Quarter =
[Fiscal Year] & "-" & [Fiscal Quarter]
暦年と会計年度の対応表(4月始まり):
| 月 | 暦年 | 暦年Quarter | 会計年度 | 会計Quarter |
|---|---|---|---|---|
| 2023年4月 | 2023 | Q2 | FY2023 | FQ1 |
| 2023年7月 | 2023 | Q3 | FY2023 | FQ2 |
| 2023年10月 | 2023 | Q4 | FY2023 | FQ3 |
| 2024年1月 | 2024 | Q1 | FY2023 | FQ4 |
| 2024年4月 | 2024 | Q2 | FY2024 | FQ1 |
12 実務で使うパターン集
12.1 売上分析ダッシュボードの標準メジャーセット
実務では、以下のようなメジャーセットを標準的に作成することが多い。これらをテンプレートとして用意しておくと、新しいプロジェクトでも素早く分析を始められる。
DEFINE
-- ========================================
-- 基本メジャー
-- ========================================
MEASURE Sales[Sales Amount] =
SUMX (
Sales,
Sales[Quantity] * Sales[Net Price]
)
-- ========================================
-- 累計系(Year/Quarter/Month To Date)
-- ========================================
-- 年初来累計
MEASURE Sales[YTD Sales] =
TOTALYTD([Sales Amount], 'Date'[Date])
-- 四半期初来累計
MEASURE Sales[QTD Sales] =
TOTALQTD([Sales Amount], 'Date'[Date])
-- ========================================
-- 前年比較(Year over Year)
-- ========================================
-- 前年同期の売上
MEASURE Sales[PY Sales] =
CALCULATE (
[Sales Amount],
SAMEPERIODLASTYEAR('Date'[Date])
)
-- 前年比成長率:(今年 - 前年) / 前年
MEASURE Sales[YoY Growth %] =
VAR CurrentSales = [Sales Amount]
VAR PreviousSales = [PY Sales]
RETURN
DIVIDE(CurrentSales - PreviousSales, PreviousSales)
-- 前年比差額:今年 - 前年
MEASURE Sales[YoY Diff] =
[Sales Amount] - [PY Sales]
-- ========================================
-- 前年累計比較(YTD Year over Year)
-- ========================================
-- 前年同期の年初来累計(正しい計算方法)
MEASURE Sales[PY YTD Sales] =
CALCULATE (
[Sales Amount],
DATESYTD(SAMEPERIODLASTYEAR('Date'[Date]))
)
-- 年初来累計の前年比成長率
MEASURE Sales[YTD YoY Growth %] =
DIVIDE([YTD Sales] - [PY YTD Sales], [PY YTD Sales])
-- ========================================
-- 前月比較(Month over Month)
-- ========================================
-- 前月の売上
MEASURE Sales[PM Sales] =
CALCULATE (
[Sales Amount],
DATEADD('Date'[Date], -1, MONTH)
)
-- 前月比成長率
MEASURE Sales[MoM Growth %] =
DIVIDE([Sales Amount] - [PM Sales], [PM Sales])
-- ========================================
-- 移動平均
-- ========================================
-- 3ヶ月移動平均
MEASURE Sales[3M Moving Avg] =
DIVIDE(
CALCULATE(
[Sales Amount],
DATESINPERIOD('Date'[Date], MAX('Date'[Date]), -3, MONTH)
),
3
)
EVALUATE
SUMMARIZECOLUMNS (
'Date'[Year],
'Date'[Month Number],
"Sales", FORMAT([Sales Amount], "$#,##0"),
"YTD", FORMAT([YTD Sales], "$#,##0"),
"PY Sales", FORMAT([PY Sales], "$#,##0"),
"YoY %", FORMAT([YoY Growth %], "+#0.0%;-#0.0%;-"),
"YoY Diff", FORMAT([YoY Diff], "$+#,##0;$-#,##0;$0"),
"MoM %", FORMAT([MoM Growth %], "+#0.0%;-#0.0%;-"),
"3M Avg", FORMAT([3M Moving Avg], "$#,##0")
)
ORDER BY 'Date'[Year], 'Date'[Month Number]
12.2 メジャーセットの使い方
上記のメジャーは、以下のような分析シナリオで活用できる:
| メジャー | 主な用途 |
|---|---|
| YTD Sales | 年間目標に対する進捗確認 |
| YoY Growth % | 前年との成長比較(季節性を排除) |
| YoY Diff | 前年との絶対額の差 |
| PY YTD Sales | 同時期までの累計比較 |
| MoM Growth % | 短期トレンドの把握 |
| 3M Moving Avg | 変動を平滑化したトレンド分析 |
13 タイムインテリジェンス関数のまとめ
13.1 関数の分類と内部構造
多くのタイムインテリジェンス関数は、基本関数の**シンタックスシュガー(syntax sugar)**である。内部構造を理解すると、関数の動作を予測しやすくなる。
| 関数 | 内部実装 | 用途 |
|---|---|---|
| TOTALYTD | CALCULATE + DATESYTD | 年初来累計 |
| TOTALQTD | CALCULATE + DATESQTD | 四半期初来累計 |
| TOTALMTD | CALCULATE + DATESMTD | 月初来累計 |
| SAMEPERIODLASTYEAR | DATEADD(..., -1, YEAR) | 前年同期 |
| PREVIOUSMONTH | PARALLELPERIOD(FIRSTDATE(...), -1, MONTH) | 前月全体 |
| PREVIOUSQUARTER | PARALLELPERIOD(FIRSTDATE(...), -1, QUARTER) | 前四半期全体 |
| PREVIOUSYEAR | PARALLELPERIOD(FIRSTDATE(...), -1, YEAR) | 前年全体 |
| NEXTMONTH | PARALLELPERIOD(LASTDATE(...), 1, MONTH) | 翌月全体 |
13.2 使い分けの指針
| 分析要件 | 推奨関数 | 備考 |
|---|---|---|
| 年初来累計 | TOTALYTD | 会計年度は第4引数で指定 |
| 前年同月比 | SAMEPERIODLASTYEAR | DATEADDでも可 |
| 前月比 | DATEADD(..., -1, MONTH) | |
| 前年全体との比較 | PARALLELPERIOD(..., -1, YEAR) | 年レベルで異なる動作 |
| 移動平均 | DATESINPERIOD + CALCULATE | MAX関数と組み合わせ |
| 固定期間の抽出 | DATESBETWEEN | 絶対的な日付指定 |
| 会計年度累計 | TOTALYTD + 第4引数 | "3/31"など |
| 在庫・残高(半加算) | LASTDATE / LASTNONBLANK | 期末値を取得 |
13.3 よくある間違いと解決方法
間違い1:日付テーブルがない、または不完全
タイムインテリジェンス関数を使う前に、以下を確認すること:
- 日付テーブルが存在する
- 「日付テーブルとしてマーク」されている
- 1月1日〜12月31日の全日付が含まれている
間違い2:DATEADDとPARALLELPERIODの混同
| 関数 | 月単位フィルタ時 | 使い分け |
|---|---|---|
| DATEADD(..., -1, YEAR) | 前年同月 | 同じ粒度での比較 |
| PARALLELPERIOD(..., -1, YEAR) | 前年全体 | 完全な期間との比較 |
間違い3:PREVIOUSYEARの合計行での動作
PREVIOUSYEARは内部でFIRSTDATEを使うため、複数年が選択されていると予期しない結果になる。合計行での動作を必ず確認すること。
間違い4:前年データがないときの処理
最初の年には前年データがないため、YoY計算がBLANKになる。必要に応じてIF文で処理を追加する。
-- 前年がない場合やゼロの場合を考慮した安全な計算
MEASURE Sales[YoY Growth Safe] =
VAR Current = [Sales Amount]
VAR Previous = [PY Sales]
RETURN
IF (
ISBLANK(Previous) || Previous = 0,
BLANK(), -- または 0、"N/A" など
DIVIDE(Current - Previous, Previous)
)
間違い5:PY YTD Sales の計算式
以下の式は正しく動作しない場合がある:
-- 誤り:YTD Salesメジャー内のTOTALYTDとフィルタが干渉する
MEASURE Sales[PY YTD Sales Wrong] =
CALCULATE (
[YTD Sales],
SAMEPERIODLASTYEAR('Date'[Date])
)
正しい計算式:
-- 正しい:DATESYTDとSAMEPERIODLASTYEARを組み合わせる
MEASURE Sales[PY YTD Sales] =
CALCULATE (
[Sales Amount],
DATESYTD(SAMEPERIODLASTYEAR('Date'[Date]))
)
参考文献と学習リソース
-
『The Definitive Guide to DAX (2nd Edition)』 Chapter 8: Time Intelligence
- Marco Russo & Alberto Ferrari
- https://www.sqlbi.com/books/the-definitive-guide-to-dax-2nd-edition/
-
SQLBI - Differences between DATEADD and PARALLELPERIOD in DAX
-
SQLBI Time Intelligence
-
DAX Patterns - Time Patterns
-
DAX.guide
















