🧩 実務で困ったこと
Redash で集計クエリを書くことになりました。そのクエリで様々なセグメントでの集計を行えるように、セグメントを可変にしたいです。
サンプルデータ
| segment | user_id | date | page | device | action | item_id | item_category |
|---|---|---|---|---|---|---|---|
| A | u001 | 2025-11-01 | home | pc | view | i001 | drama |
| A | u002 | 2025-11-01 | search | mobile | click | i002 | anime |
| A | u003 | 2025-11-02 | detail | mobile | view | i003 | anime |
| B | u004 | 2025-11-01 | home | tv | view | i004 | news |
サンプルデータ生成クエリ
WITH events AS (
SELECT * FROM UNNEST([
STRUCT('A' AS segment, 'u001' AS user_id, DATE '2025-11-01' AS date, 'home' AS page, 'pc' AS device, 'view' AS action, 'i001' AS item_id, 'drama' AS item_category),
STRUCT('A', 'u002', DATE '2025-11-01', 'search', 'mobile', 'click', 'i002', 'anime'),
STRUCT('A', 'u003', DATE '2025-11-02', 'detail', 'mobile', 'view', 'i003', 'anime'),
STRUCT('B', 'u004', DATE '2025-11-01', 'home', 'tv', 'view', 'i004', 'news'),
STRUCT('B', 'u005', DATE '2025-11-01', 'detail', 'pc', 'click', 'i005', 'drama'),
STRUCT('B', 'u006', DATE '2025-11-02', 'search', 'mobile', 'view', 'i006', 'drama'),
STRUCT('C', 'u007', DATE '2025-11-01', 'home', 'pc', 'view', 'i007', 'music'),
STRUCT('C', 'u008', DATE '2025-11-02', 'detail', 'mobile', 'click', 'i008', 'drama'),
STRUCT('C', 'u009', DATE '2025-11-02', 'watch', 'tv', 'view', 'i009', 'drama'),
STRUCT('D', 'u010', DATE '2025-11-01', 'home', 'pc', 'view', 'i010', 'news'),
STRUCT('D', 'u011', DATE '2025-11-01', 'search', 'mobile', 'click', 'i011', 'music'),
STRUCT('D', 'u012', DATE '2025-11-02', 'detail', 'mobile', 'view', 'i012', 'anime'),
STRUCT('E', 'u013', DATE '2025-11-01', 'home', 'pc', 'view', 'i013', 'drama'),
STRUCT('E', 'u014', DATE '2025-11-02', 'detail', 'mobile', 'click', 'i014', 'anime'),
STRUCT('E', 'u015', DATE '2025-11-02', 'watch', 'tv', 'view', 'i015', 'drama')
])
)
SELECT * FROM events;
💭 試したこと
1. 各セグメントの出し分けを boolean パラメータで制御する
以下のようなクエリを書き、{{SEG_XXX}}パラメータを TRUE, FALSE を選択できるようにすれば、任意のセグメントで集計ができそうです。
SELECT
IF({{SEG_DATE}}, date, NULL) AS date,
IF({{SEG_USER_TYPE}}, user_type, NULL) AS user_type,
IF({{SEG_PAGE}}, page, NULL) AS page,
IF({{SEG_DEVICE}}, device, NULL) AS device,
IF({{SEG_ACTION}}, action, NULL) AS action,
IF({{SEG_ITEM_ID}}, item_id, NULL) AS item_id,
IF({{SEG_ITEM_CATEGORY}}, item_category, NULL) AS item_category,
COUNT(1) AS cnt,
FROM
events
GROUP BY
date,
user_type,
page,
device,
action,
item_id,
item_category
;
一見便利ですが、セグメントが多いとクエリパラメータが膨れ上がり、運用が煩雑になります。
実際に Redash の利用者が操作に迷ったり、設定を誤って想定外の結果を出すケースがありました。

2. 集計セグメントを1つのクエリパラメータで設定する
クエリパラメータが多すぎる問題を解決するため、次の方法を試してみました。
DECLARE __segments ARRAY<STRING> DEFAULT [{{SEGMENTS}}];
DECLARE __seg_date BOOLEAN DEFAULT (SELECT "date" IN UNNEST(__segments));
DECLARE __seg_user_type BOOLEAN DEFAULT (SELECT "user_type" IN UNNEST(__segments));
DECLARE __seg_page BOOLEAN DEFAULT (SELECT "page" IN UNNEST(__segments));
DECLARE __seg_device BOOLEAN DEFAULT (SELECT "device" IN UNNEST(__segments));
DECLARE __seg_action BOOLEAN DEFAULT (SELECT "action" IN UNNEST(__segments));
DECLARE __seg_item_id BOOLEAN DEFAULT (SELECT "item_id" IN UNNEST(__segments));
DECLARE __seg_item_category BOOLEAN DEFAULT (SELECT "item_category" IN UNNEST(__segments));
SELECT
IF(__seg_date, date, NULL) AS date,
IF(__seg_user_type, user_type, NULL) AS user_type,
IF(__seg_page, page, NULL) AS page,
IF(__seg_device, device, NULL) AS device,
IF(__seg_action, action, NULL) AS action,
IF(__seg_item_id, item_id, NULL) AS item_id,
IF(__seg_item_category, item_category, NULL) AS item_category,
COUNT(1) AS cnt,
FROM
events
GROUP BY
date,
user_type,
page,
device,
action,
item_id,
item_category
;
この方法もしばらく活用してみましたが、
- クエリ冒頭に大量の
DECLARE句を書く必要があり面倒 - 集計に使用しないセグメントが出力に含まれ、妙に横長で見づらい
という課題がありました。
🧮 最終的に採用したこと
- クエリ構造をシンプルにする
- 出力をシンプルにする
を両立させるため、最終的に
3. 任意のセグメントをクエリに埋め込む方法
に落ち着きました。
クエリとクエリパラメータ設定は以下の通り。
SELECT
IF({{SEG_DATE}}, date, NULL) AS date,
{{SEGMENTS}},
COUNT(1) AS cnt,
FROM
events
GROUP BY
{{SEGMENTS}},
date
ORDER BY
{{SEGMENTS}},
date
Redash パラメータ設定で Quotation=None にすれば、例えば {{SEGMENTS}} に user_type, page を設定すると、次のようなクエリが実行されます。
SELECT
IF({{SEG_DATE}}, date, NULL) AS date,
user_type,
page,
COUNT(1) AS cnt,
FROM
events
GROUP BY
user_type,
page,
date
ORDER BY
user_type,
page,
date
セグメントで何も指定したくない場合は、 -- を選択すればOKです。ただし、GROUP BY 句のキーが空になることを防ぐため、時間セグメントは別途クエリパラメータで設定できるようにしておきます。
結果、クエリはシンプルになり集計結果も選択したセグメントのみ出力可能になりました。
Quotation=None は入力値がそのまま SQL に埋め込まれます。
本記事ではリスト選択のみ許可にしているため安全ですが、
パラメータを自由入力にすると SQL インジェクションのリスクがある点にご注意ください。



