【Looker】同一フィールドでのAND条件フィルター
以前、【Looker】アソシエーション分析的なものを実現してみる という記事の中で、Lookerでアソシエーション分析的なことを実現する方法を紹介させていただきましたが、別の実装方法を考えたので紹介したいと思います。
実現したいこと
- ユーザーID, 購入した商品(1:n)のようなデータがあったときに、商品Aと商品B、商品Cを買ったユーザーで絞り込みを行いたい
- 前回の実装方法の場合、2つまでであれば実現可能だが、3つ以上になるとできない
- 今回紹介する方法では、3つ以上の場合でもAND条件で絞り込みが可能
実装方法について紹介していきます。
利用するテーブル
前回同様にsales_orderをいう受注テーブルを元に解説していきます。
ベースとなるView
view: sales_order {
sql_table_name: `sales_order` ;;
# 注文ID
dimension: order_id {
type: number
primary_key: yes
sql: ${TABLE}.order_id ;;
}
# ユーザID
dimension: user_id {
type: number
sql: ${TABLE}.user_id ;;
}
# 注文日時
dimension_group: order_at {
type: time
timeframes: [
raw,
time,
date,
week,
month,
year
]
sql: ${TABLE}.order_at ;;
}
# 商品ID
dimension: product_id {
type: number
sql: ${TABLE}.product_id ;;
}
# 商品カテゴリ
dimension: product_category {
type: string
sql: ${TABLE}.product_category ;;
}
# 売上
measure: sales {
type: sum
sql: ${TABLE}.sales ;;
}
# ユーザー数
measure: count_users {
type: count_distinct
sql: ${user_id} ;;
}
}
実現のアイデア
ユーザーID単位に、ユニークな商品ID数をカウントします。
商品ID数をカウントする際に、指定した条件でフィルタします。例えば、A,B,Cの3つを商品で絞り込みたい場合、この3つでフィルタしたユニークな商品ID数をカウントします。
そうすると、A,B,Cのすべてを購入したユーザーの商品ID数は3となります。AとB、BとCなど2つを購入したユーザーの商品ID数は2に、A,B,Cのどれも購入していないユーザーの商品ID数は0となります。
この商品ID数が、3となるユーザー = 商品A,B,Cの3つを買ったユーザーとなります。
実現方法について
実際にLookMLとともに実装方法について解説していきます。
フィルタ用のviewを作成する
以下のようなviewを作成します。
view: sales_order_user_filter {
derived_table: {
sql:
SELECT
user_id,
count(distinct
IF({% condition filter_product_id %} product_id {% endcondition %}, product_id, NULL)
) as count_product
FROM `sales_order`
GROUP BY user_id
HAVING
count_product = {{ _filters['sales_order_user_filter.filter_product_id'] | split:"," | size }}
;;
}
# 商品IDフィルタ
filter: filter_product_id {
type: number
group_label: "User Level Filter"
suggest_dimension: sales_order.product_id
}
# ユーザID
dimension: user_id {
type: number
sql: ${TABLE}.user_id ;;
primary_key: yes
hidden: yes
}
}
viewにINNER JOINする
exploreで元のsales_orderのviewに以下のようにINNER JOINします。
explore: sales_order {
view_label: "sales order"
join: sales_order_user_filter {
view_label: "sales order"
type: inner
sql_on: ${sales_order.user_id} = ${sales_order_user_filter.user_id} ;;
}
}
解説
以下の部分でfilter_product_id
で指定した商品IDの場合にのみカウントをしています。
count(distinct
IF({% condition filter_product_id %} product_id {% endcondition %}, product_id, NULL)
) as count_product
そして、HAVING句で上記でカウントした数と、filter_product_idで指定したIDの数をLiquidで取得しています。
例えば、商品A(ID=10), 商品B(ID=21), 商品B(ID=32)を指定した場合、
_filters['sales_order_user_filter.filter_product_id']
には、10,21,32
が入ります。
それを、split:","
で分割し、size
で値の数を返しています。
HAVING
count_product = {{ _filters['sales_order_user_filter.filter_product_id'] | split:"," | size }}
実行されるSQL
商品A(ID=10), 商品B(ID=21), 商品B(ID=32)を指定した場合、実行されるSQLは以下のようになります。
WITH sales_order_user_filter AS (SELECT
user_id,
count(distinct
IF(( product_id IN (10,21,32)), product_id, NULL)
) as count_product
FROM `sales_order`
GROUP BY user_id
HAVING
count_product = 3
)
SELECT
COUNT(DISTINCT sales_order.user_id ) AS sales_order_count_users
FROM `sales_order` AS sales_order
INNER JOIN sales_order_user_filter ON sales_order.user_id = sales_order_user_filter.user_id
まとめ
- 【Looker】アソシエーション分析的なものを実現してみる ではできなかった、3つ以上のAND条件もフィルターできるようになった
- 逆に、前回は「指定したユーザーを除く」ということができたが、今回の実装ではそれができない
Discussion