メタデータ基盤を整えても、非エンジニアにとって「使えそうなデータの存在」を発見すること自体が難しい。テーブル名・カラム名は技術用語、検索しても 100 件ヒット、ER 図は読めない ── ここを突破しないと、民主化は形骸化します。
本記事では、テーブル構造を知らない人が 「こういうデータがあったのか」に辿り着くための 発見 UX を、4 つの設計パターンで整理します。 すら書けない人を含む全員が、自分の業務語彙でデータに辿り着ける状態を作る。
「発見できない」典型パターン
| 利用者の心理 | 実際に起きること |
|---|---|
| 「こんなデータあるかな?でも何で検索すればいいか分からない」 | Slack でアナリストに聞く → 待ち行列 |
| 「ER図見たけどカタカナと英字でよく分からない」 | そのまま諦める |
| 「customers と users と accounts の違いが分からない」 | 間違ったテーブルで集計、誤った報告 |
| 「あの数字どこから取るんだっけ」 | 毎回アナリストに同じ質問 |
民主化のステップ図で、よく「①SQL教える → ②権限渡す → ③民主化完成」と書かれます。実は 0 ステップ目に「データの存在を発見できる」 が必要。ここが抜けると、SQL 教えても書く対象がない状態になる。
発見 UX の 4 つの設計パターン
Pattern 1: 業務語彙による「ビジネス・グロッサリ」
テーブル名やカラム名ではなく、業務側の用語で索引を作る。「 はどこから?」「 ってどう計算する?」と聞かれたとき、技術名(`fact_user_revenue.cumulative_amount`)ではなく 業務名(「顧客生涯価値」)で検索できる。
glossary: - business_term: "顧客生涯価値 (LTV)" aliases: ["LTV", "顧客 LTV", "Lifetime Value", "生涯価値"] definition: | ある顧客が初回購入から最後の購入までに支払った合計金額。 キャンセル・返金は減算後。離反後の追加購入は含む。 formula: | SUM(amount_jpy - refund_jpy) GROUP BY customer_id primary_table: fact_user_revenue primary_column: cumulative_amount owners: [team:commerce] related_terms: ["RFM", "ARPU", "離反率"]
- business_term: "離反率 (Churn Rate)" aliases: ["Churn", "解約率", "退会率"] definition: | 期初に在籍していた顧客のうち、期末までに離反した割合。 離反の定義: 90 日間購入が無い状態。 formula: "...省略..." primary_table: dim_customers primary_column: churn_flag owners: [team:retention]DataHub には Glossary Terms 機能があり、上記のように業務語彙とテーブル/カラムを紐付けられます。Atlan / Alation も同等。
Pattern 2: 自然言語検索(Slack/UI)
「売上の月次データ」「返品が多かった商品」のような 業務日本語で検索して、対応するテーブル / カラム / 既存ダッシュボードを返す。 構成が定石。
import anthropicfrom anthropic import Anthropicimport numpy as np
client = Anthropic()# 各テーブル/カラム/グロッサリを1つの「ドキュメント」として extends してから埋め込むdocs = []for term, info in glossary.items(): docs.append({ "id": f"glossary:{term}", "text": f"業務語彙: {term}\n別名: {', '.join(info['aliases'])}\n定義: {info['definition']}\nテーブル: {info['primary_table']}", })for table_name, table in metadata_tables.items(): docs.append({ "id": f"table:{table_name}", "text": f"テーブル: {table_name}\n説明: {table['description']}\nカラム: {', '.join(table['columns'].keys())}", })
# 埋め込み(実装は省略 — Voyage AI / Cohere / OpenAI などを使う)embeddings = embed([d["text"] for d in docs])
def discover(question: str, top_k: int = 5): q_emb = embed([question])[0] sims = np.dot(embeddings, q_emb) top_idx = np.argsort(sims)[::-1][:top_k] return [docs[i] for i in top_idx]
# 例print(discover("売上の月次データはどこ?"))# → glossary:LTV, table:fact_user_revenue, table:fact_orders, ...EP.04 で作った Slack Bot に 「@bot 〇〇のデータどこ?」コマンドを追加するのが現実的。質問に対して 「テーブル候補3つ + 既存ダッシュボード2つ」を返すだけで、「自分はどのテーブルを叩けばよいか」が一発でわかる。
Pattern 3: 利用シーン別の「動線」を作る
ホーム画面に「業務シーン別の入口」を配置する。テーブル一覧から探させない。
| 業務シーン | 提供する動線 |
|---|---|
| 売上の状況を見たい | → 経営ダッシュボード(Looker) + 既存テンプレ集計 |
| 顧客分析がしたい | → 顧客マスタの主要テーブル + よく使う関連クエリ |
| 施策の効果を測りたい | → A/B テスト集計テンプレート + 効果測定 Streamlit |
| 在庫を確認したい | → 在庫テーブル + 発注リード集計テンプレート |
Pattern 4: 「他の人の使い方」を見せる
「営業部のXさんもこのテーブル使ってます」「先週 53 回参照されました」のような 社会的証明 が発見を後押しします。利用ログから自動表示。
WITH refs AS ( SELECT referenced_tables AS rt, user_email, FROM `region-us`.INFORMATION_SCHEMA.JOBS_BY_PROJECT, UNNEST(referenced_tables) AS referenced_tables WHERE creation_time >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY) AND state = 'DONE')SELECT CONCAT(rt.project_id, '.', rt.dataset_id, '.', rt.table_id) AS table_full, COUNT(DISTINCT user_email) AS unique_users, COUNT(*) AS query_countFROM refsGROUP BY 1ORDER BY unique_users DESCLIMIT 10;「 / 機密」の扱いには細心の注意
「テーブル一覧 / カラム名 / 関連語」だけを開示するのは安全だが、サンプルデータを表示するとPII漏洩リスク。プレビューを出すなら マスキング済みサンプル を別途用意しておく。Pattern 4 の利用ログ表示でも 個人特定可能なクエリ全文は出さない(ハッシュ or 集計のみ)。
ふくふくの推奨:3 ヶ月の段階導入
- 1Phase 1: ビジネス・グロッサリを で 30〜50 用語まずまとめる(最頻出指標から)
- 2Phase 2: Slack Bot に「これどこ?」コマンドを追加、自然言語検索で テーブル/グロッサリ を返す
- 3Phase 3: 利用ログを毎日集計して 「人気テーブル / 人気の組合せ」 を Looker でダッシュボード化、ホーム画面化
- 4Phase 4 (継続): グロッサリを 業務側の議論で更新する文化を作る(用語が増えるたびに DataHub に追加)
次の話
EP.09 では、発見した先の「クエリの型」。ゼロから書かせず、パラメタライズドテンプレートで再現性を担保する設計。
この記事の感想を教えてください
あなたの 1 クリックで、本当にこの記事は更新されます。「もっと詳しく」「続編希望」が一定数集まった記事は、 ふくふくが 実際に内容を拡充したり続編記事を公開 します。 送信したリアクションはお使いのブラウザに記録され、再カウントされません。