0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Redashでセグメントを可変にする:集計クエリを汎用化する設計メモ

Last updated at Posted at 2025-11-30

🧩 実務で困ったこと

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
;   

クエリパラメータは以下のように設定します。
image.png

一見便利ですが、セグメントが多いとクエリパラメータが膨れ上がり、運用が煩雑になります。
実際に Redash の利用者が操作に迷ったり、設定を誤って想定外の結果を出すケースがありました。
image.png

2. 集計セグメントを1つのクエリパラメータで設定する

クエリパラメータが多すぎる問題を解決するため、次の方法を試してみました。

  1. 集計セグメント選択は1つのクエリパラメータで選択させる
    image.png
  2. {{SEGMENTS}} パラメータをもとに各セグメントのフラグを生成し、集計に利用
    クエリは以下のようになります。
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
;

image.png

この方法もしばらく活用してみましたが、

  • クエリ冒頭に大量の 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

image.png

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 インジェクションのリスクがある点にご注意ください。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?