カスタムフィルタ+now()関数の使い方
Lookerでデータを見るとき、「いつもは当月だけど、月初だけは先月の実績を見たい」「月曜日だけ先週金曜日、それ以外は前日のデータを確認したい」といった、特定の条件でデータ抽出期間を動的に変更したいと思ったことはありませんか?
毎回手動でフィルタを調整するのは面倒ですよね。そんな悩みを解決するのが、カスタムフィルタとnow()
関数を組み合わせた方法です。
この記事では、Lookerのカスタムフィルタでnow()
関数などを活用し、分析業務をさらに効率化するための具体的な方法を、実際にビジネスサイドから要望があった2つの実践的なシナリオに沿ってご紹介します。見逃しがちな運用上の注意点もご紹介していますので、ぜひ最後までご覧ください。
はじめに:Lookerのカスタムフィルタとは?
Lookerのカスタムフィルタは、Looker Expressions(Looker式)というLooker独自の式を用いて、標準のフィルタよりも柔軟で複雑なフィルタ条件を指定できる機能です。
これにより、固定値や単純な期間を選択するだけでなく、クエリ実行時の日付や曜日などに応じて動的にフィルタ条件を変化させるといったことが可能になります。
より詳しくは、公式ドキュメントもチェックしてみてください。
https://cloud.google.com/looker/docs/filtering-and-limiting?hl=ja#custom_filters
🧪 準備:動作検証用のダミーテーブルの作成
動作検証用に、まずは日付と売上データを持つ簡単なネイティブ派生テーブル(derived_table)を準備します。
Lookerのプロジェクトに新しいビューファイル(例: sales_dummy.view.lkml
)を作成し、以下のコードを貼り付けてください。(データベースはBigQueryを使用しています。)
view: sales_dummy { derived_table: { sql: SELECT CAST('2025-06-27' AS TIMESTAMP) AS event_date, 120 AS sales UNION ALL SELECT CAST('2025-06-28' AS TIMESTAMP) AS event_date, 130 AS sales UNION ALL SELECT CAST('2025-06-29' AS TIMESTAMP) AS event_date, 140 AS sales UNION ALL SELECT CAST('2025-06-30' AS TIMESTAMP) AS event_date, 150 AS sales UNION ALL SELECT CAST('2025-07-01' AS TIMESTAMP) AS event_date, 100 AS sales UNION ALL SELECT CAST('2025-07-02' AS TIMESTAMP) AS event_date, 110 AS sales UNION ALL SELECT CAST('2025-07-03' AS TIMESTAMP) AS event_date, 120 AS sales UNION ALL SELECT CAST('2025-07-04' AS TIMESTAMP) AS event_date, 130 AS sales UNION ALL SELECT CAST('2025-07-05' AS TIMESTAMP) AS event_date, 140 AS sales UNION ALL SELECT CAST('2025-07-06' AS TIMESTAMP) AS event_date, 150 AS sales UNION ALL SELECT CAST('2025-07-07' AS TIMESTAMP) AS event_date, 160 AS sales UNION ALL SELECT CAST('2025-07-08' AS TIMESTAMP) AS event_date, 170 AS sales ;; } dimension: event_date { type: date datatype: timestamp sql: ${TABLE}.event_date ;; } dimension: sales { type: number sql: ${TABLE}.sales ;; } measure: total_sales { type: sum sql: ${sales} ;; } }
モデルファイルにこのビューを使ったExploreを追加し、以下のようにデータを表示できれば準備は完了です。
シナリオ1:月初は「前月」、それ以外は「当月」のデータを表示する
まずは、月次レポートなどで、閲覧者がフィルタを操作しなくても、月初(1日)には前月のサマリーが、2日以降は当月の進捗が自動で表示されるようにしたい、という要望に応えます。
💡 実装方法
フィルタタブを開くと、+カスタム式というボタンがあるので、これをクリックします。
するとLooker式を入力するテキストエディタが開きました。
今回は、実行日が1日の場合は先月、それ以外の場合は当月を抽出する条件で設定してみようと思います。
if( extract_days(now()) = 1, trunc_months(${sales_dummy.event_date}) = add_months(-1, trunc_months(now())), trunc_months(${sales_dummy.event_date}) = trunc_months(now()) )
実行結果の確認
記事執筆日の2025年7月9日に実行すると、条件は偽(1日ではない)となるため、当月(7月)のデータが正しく表示されます。
動作確認のため、式を extract_days(now()) = 9
に変更して(本日を月初と見なして)実行してみましょう。(実際に試される場合は、その日の日付に変更してください。)
すると、意図通り先月(6月)のデータが抽出されました。
生成されるSQL
このLooker Expressionは、BigQueryでは以下のようなCASE式に変換されます。
-- 生成されるSQLのWHERE句(一部抜粋) WHERE ( CASE EXTRACT(DAY FROM CURRENT_TIMESTAMP() AT TIME ZONE 'Asia/Tokyo') = 9 THEN -- 条件が真(今日が9日)の場合、先月のデータを取得 TIMESTAMP_TRUNC( (TIMESTAMP_TRUNC(sales_dummy.event_date , DAY, 'Asia/Tokyo')) , MONTH, 'Asia/Tokyo') = TIMESTAMP(DATETIME_ADD(DATETIME(TIMESTAMP_TRUNC(CURRENT_TIMESTAMP(), MONTH, 'Asia/Tokyo')), INTERVAL -1 MONTH)) ELSE -- 条件が偽(今日が9日以外)の場合、当月のデータを取得 TIMESTAMP_TRUNC( (TIMESTAMP_TRUNC(sales_dummy.event_date , DAY, 'Asia/Tokyo')) , MONTH, 'Asia/Tokyo') = TIMESTAMP_TRUNC(CURRENT_TIMESTAMP(), MONTH, 'Asia/Tokyo') END )
このように、Lookerが実行時の状況に応じてSQLを動的に組み立ててくれていることが分かります。
⚠️注意点:タイムゾーンの罠
now()
関数を使う上で最も注意すべき点がタイムゾーンです。
Lookerは、dimension
のdatatype
によって、生成するSQLのタイムゾーンの扱いが変わることがあります。例えば、先ほどのevent_date
ディメンションのdatatype
をtimestamp
からdate
に変更してみます。
view: sales_dummy { derived_table: { sql: SELECT CAST('2025-06-27' AS DATE) AS event_date, 120 AS sales UNION ALL SELECT CAST('2025-06-28' AS DATE) AS event_date, 130 AS sales UNION ALL SELECT CAST('2025-06-29' AS DATE) AS event_date, 140 AS sales UNION ALL SELECT CAST('2025-06-30' AS DATE) AS event_date, 150 AS sales UNION ALL SELECT CAST('2025-07-01' AS DATE) AS event_date, 100 AS sales UNION ALL SELECT CAST('2025-07-02' AS DATE) AS event_date, 110 AS sales UNION ALL SELECT CAST('2025-07-03' AS DATE) AS event_date, 120 AS sales UNION ALL SELECT CAST('2025-07-04' AS DATE) AS event_date, 130 AS sales UNION ALL SELECT CAST('2025-07-05' AS DATE) AS event_date, 140 AS sales UNION ALL SELECT CAST('2025-07-06' AS DATE) AS event_date, 150 AS sales UNION ALL SELECT CAST('2025-07-07' AS DATE) AS event_date, 160 AS sales UNION ALL SELECT CAST('2025-07-08' AS DATE) AS event_date, 170 AS sales ;; } dimension: event_date { type: date # ▼▼▼datatypeもdateに変更▼▼▼ datatype: date sql: ${TABLE}.event_date ;; } dimension: sales { type: number sql: ${TABLE}.sales ;; } measure: total_sales { type: sum sql: ${sales} ;; } }
この状態で先ほどと全く同じカスタムフィルタを適用すると、生成されるSQLの一部からタイムゾーン指定(AT TIME ZONE 'Asia/Tokyo'
)が消えてしまっていることがわかるかと思います。
-- 生成されるSQLのWHERE句(一部抜粋) WHERE ( CASE EXTRACT(DAY FROM CURRENT_TIMESTAMP() AT TIME ZONE 'Asia/Tokyo') = 9 THEN -- 条件が真(今日が9日)の場合、先月のデータを取得 TIMESTAMP_TRUNC(TIMESTAMP( (TIMESTAMP(sales_dummy.event_date )) ), MONTH) = TIMESTAMP(DATETIME_ADD(DATETIME(TIMESTAMP_TRUNC(CURRENT_TIMESTAMP(), MONTH)), INTERVAL -1 MONTH)) ELSE -- 条件が偽(今日が9日以外)の場合、当月のデータを取得 TIMESTAMP_TRUNC(TIMESTAMP( (TIMESTAMP(sales_dummy.event_date )) ), MONTH) = TIMESTAMP_TRUNC(CURRENT_TIMESTAMP(), MONTH) END )
私の環境でのCURRENT_TIMESTAMP()
のデフォルトタイムゾーンはUTCなので、日本で毎月1日の午前9時までにこのクエリを実行すると、意図せず先々月のデータを参照してしまう可能性があります。
対策
上記のようなケースでタイムゾーンを考慮した計算を行う場合は、add_hours()
関数でJSTの時差(9時間)を加えて調整してあげるようにしてください。
if( extract_days(now()) = 1, trunc_months(${sales_dummy.event_date}) = trunc_months(add_months(-1, add_hours(9,now()))), trunc_months(${sales_dummy.event_date}) = trunc_months(add_hours(9,now())) )
now()
が出力する日時は、データベースのデフォルトのタイムゾーンやLookerのタイムゾーン設定やユーザーの個別のタイムゾーン設定にも依存します。
now()
を使ってカスタムフィルタを実装した際は、必ず実際に生成されたSQLを確認し、実際にこのフィルタを利用するユーザーでsudo
するなどして挙動をテストすることをお勧めします。
シナリオ2:月曜日は「前週金曜日」、それ以外は「昨日」のデータを表示する
次に、より発展的なシナリオです。営業日のデイリーレポートなどで、月曜日にレポートを見た場合は週末を挟んだ金曜日の実績を、火〜金曜日は前日の実績を自動で表示します。
💡 実装方法
早速ですが、この条件を満たすLooker式がこちらです。(event_dateのdimensionのdatatypeはtimestampに戻しています。)
if( mod(diff_days( date(2025,1,6), date( extract_years(now()), extract_months(now()), extract_days(now()))), 7 ) = 0, ${sales_dummy.event_date} = trunc_days(add_days(-3, now())), ${sales_dummy.event_date} = trunc_days(add_days(-1, now())) )
先程のものより少し複雑なので、分解して確認していきます。
-
date(2025, 1, 6)
基準となる月曜日をdate
型で指定します。これは過去でも未来でも、どの月曜日でも構いません。 -
date(extract_years(now()), ...)
now()
から年・月・日を抽出し、date()
関数で組み合わせることで、今日のdate
型データを作成します。これにより、diff_days
の両方の引数がdate
型に揃い、正確な日数差を計算できます。 -
mod(diff_days(...), 7)
基準の月曜日から今日までの経過日数を7
で割った余りを求めます。基準が月曜日なので、余りが0
なら月曜日、1
なら火曜日、2
なら水曜日…となります。 -
if(余り = 0, ...)
余りが0
(月曜日)であれば3日前のデータを、それ以外であれば1日前のデータを抽出します。
実行結果の確認
本日(2025年7月9日)は水曜日です。基準日からの経過日数を7で割った余りは2
になるため、if
文の条件は偽となり、昨日(7月8日)のデータが正しく抽出されます。
テストとして、= 0
の部分を = 2
(水曜日)に変更して実行してみると・・・
すると、今度はif
文の条件が真となり、3日前(7月6日)のデータが抽出されました。
数字で指定した曜日のみ、3日前のデータを抽出するという条件を適用できていることが確認できました。
このように、Lookerの関数を組み合わせることで、複雑な業務要件にも柔軟に対応できます。
繰り返しになりますが、生成されたSQLが意図通りかの確認は必須です。Lookerが構築するクエリはしばし長く、冗長になってしまうこともあるので、生成AIもうまく活用するなどして細かくチェックされるのが良いかと思います。
まとめ
本記事では、Lookerのカスタムフィルタとnow()
関数などを組み合わせ、特定の条件でデータ抽出期間を動的に変更する方法を、2つの実践的なシナリオに沿ってご紹介しました。
特に重要なポイントは、now()
関数を利用する際のタイムゾーンの扱いです。dimension
のdatatype
やLookerの各種タイムゾーン設定、使用しているデータベースの設定によって、生成されるSQLが変化し、意図しない結果を招く可能性があります。
便利なカスタムフィルタですが、実装した際は必ず生成されたSQLを確認する習慣をつけ、日々の分析業務の効率化に役立ててみてください。