BEMAロゴ

エンジニアの
成長を支援する
技術メディア

【KARTE Datahub 実践】エンジニアが解説する機能紹介と大規模SQL実装の裏話

この記事は「BEMA Lab Advent Calendar 2025Open in new tab」の3日目の記事です。
※本アドベントカレンダーの3日目の投稿となります。

はじめに 

はじめまして。株式会社メンバーズ サースプラスカンパニーOpen in new tab の大友です。
本記事は、KARTEを実務で活用した経験から得た知見を整理し、導入検討者や実務担当者の参考になるポイントを共有するために執筆しました。

想定読者
KARTEOpen in new tabの導入を検討している、または利用を開始して間もない方
・SQLを書いているエンジニア・アナリストの方

記事の構成
・前半:KARTEというプロダクトをエンジニア目線で紹介
・後半:SQLの実装において実務で経験した課題と解決方法の体験談

KARTEの概要や実務でのSQL実装で得た知見が、本記事を読んだ方に少しでも参考になれば幸いです。

KARTEとは?

KARTEを一言で説明すると、
「ウェブサイトやアプリに訪れたユーザーをリアルタイムで解析・可視化し、一人ひとりに最適なユーザー体験を提供できるCXツール」です。

株式会社プレイドが提供する国産のCX (カスタマーエクスペリエンス) ツールで、大手企業から中小企業まで幅広い企業が導入しています。

KARTEの主な機能

機能が豊富であるため完全に網羅はできていませんが、メインの機能について以下の表にまとめました。特にエンジニアの方が使う機会が多い機能を重点的に書き出しています。

KARTE 機能表

No.

機能名

概要

1

KARTE WebOpen in new tab

<Insight機能>
サイトに訪問したユーザーのリアルタイム解析ができます。

N1分析で1ユーザーの行動履歴を深掘りしたり、「特定のページを見た人」といった条件でセグメント (グループ) 分けしたりと、顧客理解のための分析に活用できます。

<Action機能>
条件に合致したユーザーにだけポップアップ表示やバナー差し替えなどの施策がとても簡単に実行できます。

施策テンプレートが豊富でノーコードでも実装が可能ですが、裏側で動いているフロントエンド言語の編集も可能であるため、エンジニアが携わることでテンプレートの域を超えた独自のデザインや高度な施策も実現できます。

2

KARTE for AppOpen in new tab

KARTE Webの主要機能をモバイルアプリで実現するプロダクトです。

ウェブサイトと異なり、アプリ固有の開発環境に依存する部分が大きくなるため、エンジニアの専門知識が求められる傾向があります。

3

KARTE MessageOpen in new tab

メール・プッシュ通知・LINE配信をパーソナライズかつ効率的に配信できるMAツールです。
配信ターゲットは、クエリを使ってリストを作成できます。

4

KARTE DatahubOpen in new tab

KARTE内外のデータを自由に扱うための顧客データ基盤です。
BigQueryがベースとなっており、SQLを使って膨大なデータを自在に編集できます。
クエリを元に分析業務やメール配信リストの作成も可能です。

5

KARTE BlocksOpen in new tab

ノーコードかつ直感的な操作で、サイト運営をはじめ、ABテストやユーザーに合わせたパーソナライズ配信など、サイト改善に必要な機能も充実したツールです。

6

KARTE CraftOpen in new tab

Action機能では実現できない高度な処理が可能です。

バックエンドを拡張するかのごとく、柔軟な体験設計をフルコードで実装できます。

7

KARTE SignalsOpen in new tab

広告効果を根本的に改善する「1st Party Dataの活用」を、ワンストッププラットフォームで実現できます。Cookieレス対策と広告効果の可視化を同時に可能にするツールです。

8

QANT WebOpen in new tab

顧客が抱える課題と最適解をマッチングさせるなど、顧客の自己解決を促すWebサポートをノーコードで簡単に実現できます。サポート部門が主体となってPDCAサイクルを回し、改善を内製化できるツールです。

エンジニアの開発を加速させる機能 3選

1.  KARTE Action機能

Action機能は、マーケティング施策で多発するフロントエンドの修正工数を大幅に削減できます。ポップアップやアンケート、既存テキストの置き換えなどを、マーケターやPM(プロジェクトマネージャー)自身が、直接GUI上で実行・ABテストできるようになります。

これにより、エンジニアは「ここの文言を修正してほしい」「このバナーを差し替えてほしい」といった定型的な開発・デプロイ業務から解放され、プロダクトのコアな機能の開発に集中できるようになります。
なお、裏側で動いているフロントエンド言語を直接編集することもできるため、GUIだけでは実現できないロジックも実装可能です。

Actionの管理画面 GUI上でテキスト編集や画像の差し替えが可能
Actionの管理画面 GUI上でテキスト編集や画像の差し替えが可能
エンジニア向けの機能:裏で動いているコードも自由にカスタマイズ可能
エンジニア向けの機能:裏で動いているコードも自由にカスタマイズ可能

2. KARTE Datahub機能

KARTE内外に点在する膨大な顧客データを、SQLで自由に集計・分析できるデータ基盤です。分析用データやメール配信リストなどを柔軟に実装できます。

Datahubの主な利点として以下の項目があります
・GUI上で簡単にデータセットとテーブルが作成できます
・ジョブフロー機能で、決まった時間に自動でクエリを実行できます
・技術基盤にBigQueryを採用しています
・クエリのテンプレートが100種類以上あり、1からSQLを書かなくても活用が可能です
・社内外のデータを連携が可能で、分析の幅を広げることができます

SQLの実装画面
SQLの実装画面

3. KARTE Craft機能

KARTE Actionでは実現できない、より高度な技術をフルコードで実現できる機能です。
内蔵されているCopilotが、実現したいシナリオに応じたコードテンプレートを提示するため、ゼロからの開発工数を削減しつつ、実装の自由度も担保されます。

画面左:Craft実装画面・画面右:実装テンプレート検索画面
画面左:Craft実装画面・画面右:実装テンプレート検索画面

SQL実装の体験談

実装依頼の背景

某大手ウェブサイトに蓄積された膨大なユーザー行動データを分析し、マーケティング施策の立案や効果検証に活用できるインサイトを得ることが目的でした。

作業内容

サイトの利用状況を多角的に把握するため、合計20種類の指標を算出するためのクエリを作成する必要がありました。
サイトの利用状況を多角的に把握するため、合計で20種類(例:離脱率、特定セグメントの購買率など)の異なる指標を算出するためのクエリを作成する必要がありました。

直面した課題

・クエリリソースの制約
データ量が膨大すぎて、1指標あたりのクエリ実行で約20TBのリソースを消費することが判明しました。 KARTEの契約上、1ヶ月の実行可能クエリリソースは40TBだったため、全20指標を実行するには (20TB × 20指標 = 400TB) 、単純計算で10ヶ月を要する計算です。

・保存レコード上限について
仮にクエリが実行できたとしても、その結果セットが数十億行に達する見立てとなり、1テーブルあたりの最大レコード数を超過してしまう問題がありました。

解決方法

前提として、KARTE Datahubのクエリリソース消費量はクエリの実行時間ではなく、「抽出元のテーブルから "どのカラム" を"どれくらいの抽出期間" で抽出したか」に依存します。今回の20指標は、幸いなことに抽出元となるテーブル(4テーブル)と抽出期間(過去3ヶ月分)がすべて共通していました。
よって、1回のクエリ実行で20指標すべてを同時に算出することが可能となり、元テーブルのスキャンを1回に抑え、クエリリソースの問題を解決することができました。

なお、レコード上限の問題については、各指標のクエリ結果を上位100行に制限して抽出することで回避しています。今回の目的は分析であり、全件データを保持する必要がなく、一意に持つべきキーもないため採用できた対策でした。

具体的な実装と感想

実装内容

20指標を1回で実行するため、各指標のロジックを個別のWITH句(CTE:Common Table Expression/共通テーブル式)で定義しました。
最終的に、それら20個のCTEをUNION ALLで縦に結合するアプローチを取りましたが、指標ごとに出力カラムが異なるため、ダミーカラムを大量に作成してスキーマ (型とカラム数) を強引に合わせました。出力結果は、指標ごとに100行ずつのデータが階段状に並ぶイメージです。

WITH句で定義した各指標をUNION ALLで結合するサンプルコード

-- 指標20個分のCTEを用意する
-- 3ヶ月連続購入者の6月の閲覧ページと総数
WITH p6_view_events AS (

  SELECT
    ve.uri_url AS p6_view_uri_url,
    COUNT(*) AS p6_view_count
  FROM view_events_all ve
  JOIN purchase_flag_6_8 pf ON ve.user_id = pf.user_id
  WHERE pf.purchase_flag_6_8 = '6〜8月までの3ヶ月連続購入'
  AND sync_date >= TIMESTAMP('2025-06-01') AND sync_date < TIMESTAMP('2025-07-01')
  GROUP BY ve.uri_url
  ORDER BY p6_view_count DESC
  LIMIT 100
),

-- 3ヶ月連続購入者の7月の閲覧ページと総数
p7_view_events AS (
  SELECT
    ve.uri_url AS p7_view_uri_url,
    COUNT(*) AS p7_view_count
  FROM view_events_all ve
  JOIN purchase_flag_6_8 pf ON ve.user_id = pf.user_id
  WHERE pf.purchase_flag_6_8 = '6〜8月までの3ヶ月連続購入'
  AND sync_date >= TIMESTAMP('2025-07-01') AND sync_date < TIMESTAMP('2025-08-01')
  GROUP BY ve.uri_url
  ORDER BY p7_view_count DESC
  LIMIT 100
),
-- 中略 

-- UNION ALLで各CTEテーブルを結合するためにダミーカラムを生成し、結合する
SELECT
  p6_view_uri_url,
  p6_view_count,
  CAST(NULL AS STRING) AS p7_view_uri_url,
  CAST(NULL AS NUMERIC) AS  p7_view_count,
  CAST(NULL AS STRING) AS p8_view_uri_url,
  CAST(NULL AS NUMERIC) AS p8_view_count,
  CAST(NULL AS STRING) AS e7_exit_page,
  CAST(NULL AS NUMERIC) AS e7_exit_count,
  CAST(NULL AS STRING) AS e8_exit_page,
  CAST(NULL AS NUMERIC) AS e8_exit_count,
  CAST(NULL AS STRING) AS init_utm_campaign,
  CAST(NULL AS NUMERIC) AS init_utm_user_count,
  CAST(NULL AS STRING) AS init_medium,
  CAST(NULL AS NUMERIC) AS init_medium_user_count,
  CAST(NULL AS STRING) AS init_content,
  CAST(NULL AS NUMERIC) AS init_content_user_count,
  CAST(NULL AS STRING) AS init_source,
  CAST(NULL AS NUMERIC) AS init_source_user_count,
  CAST(NULL AS STRING) AS init_term,
  CAST(NULL AS NUMERIC) AS init_term_user_count,
  CAST(NULL AS STRING) AS july_utm_campaign,
  CAST(NULL AS NUMERIC) AS july_utm_user_count,
  CAST(NULL AS STRING) AS july_medium,
  CAST(NULL AS NUMERIC) AS july_medium_user_count,
  CAST(NULL AS STRING) AS july_content,
  CAST(NULL AS NUMERIC) AS july_content_user_count,
  CAST(NULL AS STRING) AS july_source,
  CAST(NULL AS NUMERIC) AS july_source_user_count,
  CAST(NULL AS STRING) AS july_term,
  CAST(NULL AS NUMERIC) AS july_term_user_count,
  CAST(NULL AS STRING) AS august_utm_campaign,
  CAST(NULL AS NUMERIC) AS august_utm_user_count,
  CAST(NULL AS STRING) AS august_medium,
  CAST(NULL AS NUMERIC) AS august_medium_user_count,
  CAST(NULL AS STRING) AS august_content,
  CAST(NULL AS NUMERIC) AS august_content_user_count,
  CAST(NULL AS STRING) AS august_source,
  CAST(NULL AS NUMERIC) AS august_source_user_count,
  CAST(NULL AS STRING) AS august_term,
  CAST(NULL AS NUMERIC) AS august_term_user_count
FROM p6_view_events

UNION ALL

SELECT
  CAST(NULL AS STRING) AS p6_view_uri_url,
  CAST(NULL AS NUMERIC) AS p6_view_count,
  p7_view_uri_url,
  p7_view_count,
  CAST(NULL AS STRING) AS p8_view_uri_url,
  CAST(NULL AS NUMERIC) AS p8_view_count,
  CAST(NULL AS STRING) AS e7_exit_page,
  CAST(NULL AS NUMERIC) AS e7_exit_count,
  CAST(NULL AS STRING) AS e8_exit_page,

-- 中略

感想

当初の見積もりでは全指標の実行に10ヶ月を要する見込みでしたが、このアプローチによって2ヶ月以内に全て完了させることができ、とても良い経験ができたと思っています。
WITH句ごとにロジックのコメントアウトを残すなど可読性には配慮しましたが、結果として全体が1,500行に及ぶクエリになってしまいました。リソース制約はクリアできましたが、運用向きにはさらに工夫が必要だと感じています。
理想をいえばリソースを拡大して、ロジックごとにファイルを分けるべきだなと思います。

さいごに

本記事では、KARTEの概要をエンジニア目線で解説するとともに、実務で直面したKARTE DatahubのSQL実装課題とその解決アプローチについてご紹介しました。
これからKARTEを導入する方や、SQL実装を担当しているエンジニア・アナリストの方にとって、少しでもヒントになれば嬉しいです!

最後までお読みいただき、ありがとうございました!

この記事が役に立ったと思ったら、
ぜひ「いいね」とシェアをお願いします!

リンクをコピーXでシェアするfacebookでシェアする

この記事を書いた人

大友 航平
大友 航平
2024年にメンバーズに新卒で入社。サースプラスカンパニー所属。 CXプラットフォーム「KARTE」を活用したカスタマーサクセス業務とエンジニア業務を担当していました。 現在は、お客様の意思決定をデータで支援できるアナリストを目指しています。
詳しく見る
ページトップへ戻る