第 8 章:把資料變成看得到的圖表 — Metabase BI 視覺化
這是 Phase B 的第一章。前面各章寫進 MySQL 的股價資料,在這一章轉成可互動的圖表與儀表板。
做完這一章,你會做到
- 啟動 Metabase 並連上你的 MySQL。
- 做出三種圖表:走勢折線圖、成交量長條圖、最新收盤價數字卡片。
- 把多張圖組合成一個 Dashboard。
- 用 SQL 模式下自訂查詢,並把 MySQL 的 VIEW 拿來做圖。
- 分得清「Metabase 自己的設定」和「它查詢的資料來源」是兩個不同的資料庫。
- 直接查設定庫
metabasedb,對照出自己的每個操作存成了哪些資料。
先搞懂:資料的「出口」
前面的章節都在解決「怎麼把資料可靠地生出來、存起來」。但存進資料庫的資料,目前只有會寫 SQL 的人查得到。BI(Business Intelligence,商業智慧)工具就是資料的出口——讓不會寫 SQL 的人也能拉圖表、看趨勢、做決策。
BI / Dashboard 工具在做什麼?六件核心的事:
- 連資料源:接上資料庫/倉儲,不搬資料、即時查
- 查詢:不會 SQL 的人拖拉點選(query builder),會 SQL 的人直接寫
- 視覺化:查詢結果變成折線、長條、圓餅、數字卡…
- Dashboard 組裝:多張圖拼成一面儀表板,加全域篩選器
- 分享與權限:誰能看哪個庫、哪張表、哪面板
- 排程與告警:定時寄報表、指標超標自動通知
常見 BI 工具一覽:
| 工具 | 定位 | 特點 | 費用 |
|---|---|---|---|
| Metabase | 開源自架、輕量 | 上手最快、query builder 對非工程師友善、Docker 一行起 | 開源免費(另有付費雲) |
| Power BI | 微軟企業級 | 功能深(DAX)、Office/Excel 整合強 | 個人免費、團隊付費 |
| Tableau | 視覺化旗艦 | 拖拉分析成熟、圖表類型與客製化選項最多,大型企業普遍採用 | 貴 |
| Redash | 開源、SQL-first | 每張圖就是一句 SQL,開發者取向 | 開源(近年維護趨緩) |
| Superset | Apache 開源 | 功能最全的開源選項,架設與學習較重 | 免費 |
| Looker Studio | Google 免費雲端 | 接 GA、BigQuery 特別順 | 免費 |
| Grafana | 監控/時序起家 | 主要用於系統監控指標(metrics)儀表板,非業務報表 | 開源 |
選型參考:教學與中小團隊自架 → Metabase;企業微軟生態 → Power BI;純 SQL 人 → Redash / Superset;看系統監控 → Grafana。本課選 Metabase:開源、單一容器即可啟動、query builder 入門成本最低。
Metabase 是什麼:
- 開源的商業智慧(BI)平台
- 能將資料庫裡的資料快速轉換成圖表、儀表板與報表
- 簡單易用、安裝快速,適合中小型團隊、教育場合,或作為企業內部的輕量 BI 工具
為什麼排在這裡? 因為到第 6 章為止,資料已經能穩定寫入 MySQL(含去重與冪等)。資料管線的下一步是輸出:把表裡的數據轉成圖表與儀表板。
這一章會用到的檔案
| 檔案 | 角色 | 說明 |
|---|---|---|
metabase/docker-compose-metabase.yml |
部署 | 啟動 Metabase 容器(設定庫指向 MySQL) |
metabase/init-metabase-appdb.sql |
初始化 | 建立設定庫 metabasedb(第 13 章 all 版自動執行) |
metabase/README.md |
說明 | 連線設定與操作步驟 |
example/mock_stock_price_data.sql |
模擬資料 | MySQL 沒資料時快速補一批 |
example/vw_stock_price_daily.sql |
VIEW 範例 | 每日去重的股價 VIEW(進階段落用) |
先懂一個容易混淆的觀念:Metabase 碰到兩個資料庫
Metabase 本身也需要一個資料庫。帳號、做好的 Question 與 Dashboard、查詢紀錄——這些「Metabase 自己的狀態」都要存在某個地方,存放它們的資料庫稱為設定庫(application database)。設定庫和「被查詢的資料」是兩回事,所以本章會出現兩個 database,角色完全不同:
設定庫 metabasedb |
資料來源 mydb |
|
|---|---|---|
| 存什麼 | Metabase 的帳號、Dashboard 定義、查詢紀錄 | 股價資料(TaiwanStockPrice) |
| 誰在讀寫 | Metabase 程式本身(啟動時自動建表) | Metabase 代替使用者執行查詢 |
| 用什麼帳號連 | metabase_app(完整讀寫,Step 2 建) |
metabase_ro(唯讀,Step 3.5 建) |
| 在哪裡設定 | compose 的環境變數(啟動前就要就緒) | Metabase 網頁介面手動新增(Step 4) |
| 內容消失會怎樣 | Dashboard、帳號全部消失 | 圖表查不到數據,但 Dashboard 定義還在 |
本課把兩個 database 放在同一台 MySQL(docker-compose-local.yml 起的那台)。Metabase 啟動後,可以在 phpMyAdmin 左側同時看到 metabasedb 和 mydb——點開 metabasedb,裡面是 Metabase 自動建立的幾十張表,Dashboard 的定義就存在其中。設定庫不是抽象概念,是一個看得到內容的 database。
跟預設做法的差異:不設任何 MB_DB_* 環境變數時,Metabase 會用內建的 H2 資料庫當設定庫——H2 是內嵌在 Metabase 程式裡的單檔資料庫,免安裝、免帳密,官方定位是試用場景;正式環境官方建議把設定庫放到外部資料庫(PostgreSQL 或 MySQL)。網路教學兩種做法都有:看到「先建 metabasedb 和專用帳號」的是設定庫放 MySQL(本課做法);看到「直接啟動、掛 volume 保設定」的是 H2 做法。分辨的關鍵是設定庫放在哪。
本章共出現三組帳號,各管一件事:
| 帳號 | 用在哪 | 權限範圍 |
|---|---|---|
| Metabase 管理員(Step 3 建) | 登入 http://localhost:3000 的網頁帳號 | Metabase 介面內的最高權限;本身存在 metabasedb 裡 |
metabase_app(Step 2 建) |
Metabase 連設定庫用的 MySQL 帳號 | metabasedb 完整讀寫(Metabase 要自己建表) |
metabase_ro(Step 3.5 建) |
Metabase 連資料來源用的 MySQL 帳號 | mydb 唯讀 |
一句話:mydb 是「被查的對象」,Metabase 自己的設定存在 metabasedb。 設定跟著 MySQL 的 volume 持久化,Metabase 容器刪除重建都不影響 Dashboard。
Metabase 功能地圖
做圖之前先認識整個介面有什麼。下表的範例都以本課的股價資料示範:
| 頁面 / 功能 | 在哪 | 做什麼 | 範例 |
|---|---|---|---|
| Browse data | 左側 Databases | 點開資料庫直接看每張表 | 連上後列出 mydb 的資料表(含 VIEW) |
| Question 查詢產生器 | + New → Question | 免 SQL:選表→篩選→彙總→分組 | 「各股平均收盤價」長條圖:所有股票一次畫出 |
| SQL 模式 + 變數 | + New → SQL query | 寫 SQL,{{變數}} 做互動篩選 |
WHERE stock_id = {{sid}},換股票代號即重畫走勢 |
| 視覺化切換 | 結果頁左下 Visualization | 同一份結果切折線/長條/數字卡… | 上面兩例分別存成 bar 和 line |
| Dashboard | + New → Dashboard | 卡片拼裝+全域篩選器+自動刷新 | 「台股股價儀表板」:多張卡片組裝+篩選器 |
| Collections | 左側收藏集 | 資料夾式管理 Question/Dashboard,可設權限 | 建「股價分析」收藏集歸檔 |
| X-ray 一鍵分析 | 表格頁的黃色閃電 | Metabase 自動掃資料生一組圖 | 對 TaiwanStockPrice 執行:自動產生一組摘要圖表 |
| 訂閱與警示 | Dashboard / Question 內 | 定時寄報表、數字達標告警 | 功能在 UI 裡;寄送需先設 SMTP(管理員→設定→Email),課堂不設 |
| Admin → Databases | 齒輪 → 管理員 | 資料源管理、同步 schema | Step 10 的 VIEW 同步就在這 |
| Admin → People / Permissions | 同上 | 使用者、群組、誰能看哪個資料庫 | 見下方「權限有兩層」 |
權限有兩層:MySQL 的
metabase_ro管「Metabase 整體拿得到什麼」(DB 層);Metabase 的 Permissions 管「哪個使用者看得到什麼」(BI 層)。兩層各自獨立生效。
一步一步跟著做
Step 1:確認 MySQL 裡有資料
先確保前面幾章已經把股價寫進 mydb(第 5 章跑過 producer;注意第 6 章寫的是另一張表 TaiwanStockPrice_duplicate,不算數)。
docker compose -f docker-compose-local.yml up -d mysql phpmyadmin
docker exec mysql mysql -uroot -p1234 mydb -e \
"SELECT stock_id, COUNT(*) AS cnt FROM TaiwanStockPrice GROUP BY stock_id"
✅ 查詢列出各股票代號與筆數,代表
mydb有資料。💡 沒資料(或想要更多支股票)? 用專案附的模擬資料補入——10 支股票、各約 32 個交易日:
docker exec -i mysql mysql -uroot -p1234 mydb < example/mock_stock_price_data.sql再跑一次上面的查詢,就會看到 2330、0050、2317 等 10 支股票的資料。
Step 2:把兩個 compose 檔串起來,啟動 Metabase
本章的服務分屬兩個 compose 檔:MySQL 在 docker-compose-local.yml(Step 1 已啟動),Metabase 在 metabase/docker-compose-metabase.yml。要讓 Metabase 成功連上 MySQL,得先解決兩件事:
第一件事——網路:兩個 compose 檔預設是兩個隔離的網路。 每個 compose 檔啟動時會建立自己的網路,容器只能跟同一個網路裡的容器互通。docker-compose-local.yml 的 mysql 在它自己的預設網路裡;Metabase 的 compose 則宣告使用外部網路 my_network。兩邊要通,就把 mysql 也接上 my_network——docker network connect 做的就是這件事(一個容器可以同時掛在多個網路上,接上新網路不影響原本的):
docker-compose-local.yml 的預設網路 my_network(外部網路)
┌─────────────────────────┐ ┌──────────────────┐
│ mysql phpmyadmin │ │ metabase │
└─────┬───────────────────┘ └────────┬─────────┘
│ │
└── docker network connect ──► 接上後 mysql 同時在兩個網路,
metabase 才找得到它
第二件事——設定庫:Metabase 只會自己建「表」,不會建 database 和帳號。 上一節說過設定庫 metabasedb 放在 MySQL。Metabase 第一次啟動時會在裡面自動建立它需要的所有表,但 metabasedb 這個 database 本身、和連線用的帳號 metabase_app,MySQL 不會憑空生出來——要先用 root 跑一段 SQL 準備好。這就是啟動 Metabase 之前得先動 MySQL 的原因。
兩件事都處理完才能起 Metabase,所以順序固定:MySQL 就緒(Step 1)→ 建設定庫與帳號 → 接網路 → 起 Metabase。順序反了,Metabase 開機連不到設定庫,容器會直接結束。完整指令:
# 1. 建立設定庫與專用帳號(IF NOT EXISTS:重複執行沒有副作用)
docker exec mysql mysql -uroot -p1234 -e "
CREATE DATABASE IF NOT EXISTS metabasedb;
CREATE USER IF NOT EXISTS 'metabase_app'@'%' IDENTIFIED BY '1234';
GRANT ALL PRIVILEGES ON metabasedb.* TO 'metabase_app'@'%';"
# '帳號'@'%' 的 @ 後面是「允許從哪裡連入」,% = 任何主機都接受
# (Metabase 從另一個容器連入,來源是容器 IP,所以不能限 localhost)
# 2. 建立外部網路(建過就跳過)
docker network create my_network
# 3. 把 MySQL 接上 my_network —— Metabase 掛在 my_network 上,
# 不接的話它開機連不到設定庫(已接過會顯示 already exists,無妨)
docker network connect my_network mysql
# 4. 啟動 Metabase
docker compose -f metabase/docker-compose-metabase.yml up -d
💡
@是 MySQL 帳號的來源限制。MySQL 的帳號識別不是只有名字,而是「使用者名 + 連入來源」的組合:'metabase_app'@'%'和'metabase_app'@'localhost'是兩個不同的帳號,可以各有密碼與權限。%是萬用字元,任何來源都接受。本課用%的原因:Metabase 從另一個容器連入,來源是 Docker 網路裡的容器 IP(容器重建後會變動),限成localhost就連不進來。正式環境可收緊為子網段(如@'172.18.0.%')。排錯提示:帳密正確但來源不符時,錯誤訊息是Access denied for user 'xxx'@'實際來源'——看@後面顯示的來源就知道被哪一層擋下。💡
metabase_app對metabasedb是完整讀寫權限(GRANT ALL ... ON metabasedb.*)——Metabase 要在裡面自己建表。它跟 Step 3.5 的唯讀帳號metabase_ro用途不同:一個管設定庫、一個管資料來源,權限範圍也互不重疊。💡
docker-compose-all.yml(第 13 章)把所有服務放在同一網路,並在 MySQL volume 初始化時自動建metabasedb,上面的步驟 1-3 都不需要。
⏳ Metabase 是 Java(JVM)應用,啟動需要一段時間。用這段指令等它就緒:
until curl -s -o /dev/null -w '%{http_code}' http://localhost:3000/api/health | grep -q 200; do echo "等待 Metabase 啟動中..." sleep 10 done echo "Metabase 已就緒!"
✅ 就緒後到 phpMyAdmin 看一眼:左側多了 metabasedb,裡面有 Metabase 自動建立的表——上一節說的「設定庫」就是它。
Step 3:首次設定,建立管理員帳號
打開 http://localhost:3000 ,跟著導覽建立一個管理員帳號(這是 Metabase 網頁的登入帳號,存在設定庫 metabasedb 裡;它不是 MySQL 的使用者帳號)。語言可選繁體中文。
Step 3.5:幫 Metabase 建一個「唯讀」資料庫帳號
Metabase 對 MySQL 只需要「讀」——正式做法是給它一個只有 SELECT 的專用帳號(最小權限原則,補充C 第 6 節的實戰應用):
CREATE USER 'metabase_ro'@'%' IDENTIFIED BY 'metabase'; -- _ro = read-only 的慣用命名
GRANT SELECT ON mydb.* TO 'metabase_ro'@'%'; -- 只給查詢,不給寫
phpMyAdmin 版(同一件事的圖形操作):root 登入 → 首頁 User accounts → Add user account → 使用者名稱
metabase_ro、主機名稱選「任何主機(%)」、設密碼 → 「資料庫的權限」選mydb→ 只勾 SELECT → 執行。
✅ 這個帳號透過 Metabase 查資料一切正常;執行 DELETE 會被 MySQL 拒絕(DELETE command denied to user 'metabase_ro')——BI 只需要讀取,寫入操作在 DB 權限層即被擋下。
Step 4:把 MySQL 加進來當資料來源
在 Metabase 裡:設定(齒輪)→ 管理員 → 資料庫 → 新增資料庫,填入:
| 欄位 | 填什麼 |
|---|---|
| 類型 | MySQL |
| 顯示名稱 | Stock MySQL(自訂即可) |
| Host | mysql(注意:是這個服務名,不是 127.0.0.1) |
| Port | 3306 |
| 資料庫名稱 | mydb |
| 帳號 | metabase_ro(Step 3.5 建的唯讀帳號) |
| 密碼 | metabase |
✅ 儲存後沒報錯、能在 Metabase 裡看到
TaiwanStockPrice這張表,就連通了(Metabase 在「儲存」當下就會實際連線驗證,帳密錯會直接報錯)。💡 用
root / 1234也連得上,但正式做法是最小權限——就算 Metabase 設定錯誤或帳號外洩,資料也不會被改動。⚠️ 最常卡在 Host:在 Docker 網路裡,容器之間要用服務名(
mysql)互相找,不能用127.0.0.1(那會指向 Metabase 容器自己)。前提是兩個容器在同一個網路(my_network)。
Step 4.5:先規劃,再動手 — Dashboard 藍圖
做圖前先確定五件事:
- 每個 Question 就是一張圖
- 每張圖用來呈現什麼資訊
- Dashboard 要有哪幾張圖
- 每張圖怎麼排版
- 要有哪些 Filter、套用在哪些圖
本課的「台股股價儀表板」藍圖(Step 5-8 會做其中三張,其餘留在「換你試試看」):
Filter: 日期區間 Filter: 股票代號
┌──────────┬──────────┬──────────┬──────────┐
│ 最新收盤價 │ 總成交量 │ 追蹤股票數 │ 最新資料日期│ ← 數字卡 ×4
├──────────┴──────────┴──────────┴──────────┤
│ 收盤價走勢(折線,可多股比較) │
├─────────────────────┬──────────────────────┤
│ 各股成交量比較(長條) │ 單股高低價區間(線/區域) │
├─────────────────────┴──────────────────────┤
│ 每日行情明細(表格卡) │
└─────────────────────────────────────────────┘
Step 5:第一張圖 — 台積電收盤價折線圖
- 點「+ 新增」→「問題」(Ask a question)→ 選
TaiwanStockPrice這張表。 - 篩選:
stock_id=2330。 - 彙總:選「Average of close」;群組依據:選
date(按日)。 - 點「視覺化」→ 選折線圖。
✅ 出現台積電的收盤價走勢折線——MySQL 裡的股價資料完成第一次視覺化。
- 點「儲存」,命名「台積電收盤價走勢」→ 位置選「股價分析」收藏集(沒有就先「+ 新增收藏集」)。存檔後會問「要加進 Dashboard 嗎?」——先按「稍後」,Step 8 再一起組。
認識結果頁的三個按鈕(都在圖表區周圍):
- 左下「視覺化」:同一份結果切換折線/長條/數字卡…
- 右下「表格⇄圖表」切換:一鍵在「原始資料」和「圖」之間切換——檢查圖畫得對不對就靠它
- 右下「下載」(雲朵/箭頭圖示):把結果匯出 CSV / Excel / JSON
Step 6:第二張圖 — 各股成交量長條圖
- 「+ 新增」→「問題」→ 選
TaiwanStockPrice。 - 彙總:選「Sum of Trading_Volume」;群組依據:選
stock_id。 - 視覺化 → 長條圖。
- 儲存為「各股成交量比較」。
Step 7:第三張圖 — 最新收盤價數字卡片
- 「+ 新增」→「問題」→ 選
TaiwanStockPrice。 - 篩選:
stock_id=2330;排序:date降冪;限制 1 筆。 - 只留
close欄位。 - 視覺化 →「數字」。
- 儲存為「台積電最新收盤價」。
Step 8:組一個 Dashboard(照 Step 4.5 的藍圖)
- 點「+ 新增」→「Dashboard」,命名「台股股價儀表板」,位置一樣選「股價分析」。
- 進入編輯模式(右上鉛筆圖示)→ 上方「+」(加卡片)→ 把剛才三張圖加進來。
- 拖拉排版:抓卡片本體移動位置、抓右下角拉大小——照藍圖:數字卡放最上排、折線圖全寬。
- 加篩選器:上方漏斗圖示 → 「時間」→ 套到折線圖的
date欄位(點卡片上出現的下拉選欄位);再加一個「文字」篩選器 → 套到stock_id。之後在 Dashboard 上選日期區間或輸入股票代號,所有連結的圖會一起變。 - 加文字卡片:上方 T 圖示——放標題或說明文字,補充圖表的閱讀脈絡。
- 分頁標籤:Dashboard 上方「+新增分頁」——圖太多時分成「總覽」「個股」兩頁。
- 右上「儲存」離開編輯模式。
- 定期刷新:Dashboard 檢視模式右上時鐘圖示 → 選自動刷新間隔(如 15 分鐘),Dashboard 會定時重新查詢資料。
✅ 一個頁面同時顯示走勢、成交量、最新價;篩選器改變條件時,所有連結的卡片一起更新。
💡 公開分享:管理員 → 設定 → 公開分享 開啟後,Question/Dashboard 的分享圖示能產生「不用登入就能看」的公開連結。方便,但任何人拿到連結都能看到資料——正式環境需評估後再啟用。(按鈕名稱以 Metabase v0.62 為準,新版可能微調。)
Step 9:SQL 模式 — 直接下 SQL
Metabase 除了點選式查詢,也能直接寫 SQL:
- 「+ 新增」→「SQL 查詢」→ 選「Stock MySQL」。
- 貼上:
SELECT stock_id, date, close, Trading_Volume
FROM TaiwanStockPrice
WHERE stock_id = '2330'
ORDER BY date DESC
LIMIT 10;
- 執行(Ctrl+Enter)。
✅ 預期:台積電最近 10 筆交易資料。點選式做不到的複雜查詢,都可以退回 SQL 模式解決。
Step 10(進階):用 VIEW 做圖
VIEW 是「存起來的查詢」,可以像表一樣用。專案附了一個每日去重的股價 VIEW:
docker exec -i mysql mysql -uroot -p1234 mydb < example/vw_stock_price_daily.sql
然後讓 Metabase 認識這個新 VIEW:設定 → 管理員 → 資料庫 → Stock MySQL → 「同步資料庫 schema」。
幾秒後,用 SQL 模式做一張多股比較圖:
SELECT stock_id, trade_date, close
FROM vw_stock_price_daily
WHERE stock_id IN ('2330', '0050', '2317')
ORDER BY trade_date;
視覺化 → 折線圖,X 軸 trade_date、Y 軸 close、分組 stock_id。
✅ 預期:三支股票的收盤價走勢疊在同一張圖上。
💡 這個 VIEW 用
ROW_NUMBER()對「同股票同日期」去重——如果你的表裡有第 5 章重跑造成的重複資料,VIEW 查出來的仍是乾淨的。這是「不動原始資料、用查詢層清理」的實務手法,第 15 章 BigQuery 會再用一次。
Step 11(進階):查設定庫——每個操作都變成了資料
Step 2 建的 metabasedb,做到這裡已經累積了本章所有操作的紀錄。用 root 直接查它,把「設定庫」從概念變成查得到的資料(--default-character-set=utf8mb4 讓中文名稱正常顯示,少了它中文會變 ?):
docker exec mysql mysql -uroot -p1234 --default-character-set=utf8mb4 metabasedb -e "
SELECT id, name FROM report_dashboard; -- 你建的 Dashboard
SELECT id, name, display FROM report_card ORDER BY id DESC LIMIT 5; -- 你存的 Question 與圖表類型
SELECT dashboard_id, card_id, \`row\`, col, size_x, size_y
FROM report_dashboardcard; -- 卡片在 Dashboard 上的版面
SELECT context, card_id, COUNT(*) AS runs
FROM query_execution GROUP BY context, card_id; -- 查詢執行紀錄
"
操作與表的對照:
| 你做過的事 | 存在哪張表 | 存了什麼 |
|---|---|---|
| 存 Question(Step 5-7、9) | report_card |
查詢定義+圖表類型。點選式存的是 MBQL(Metabase 的查詢中間格式);SQL 模式存的是原生 SQL 字串,原文可在 dataset_query 欄位裡看到 |
| 建 Dashboard(Step 8) | report_dashboard |
名稱、擁有者、建立時間 |
| 把卡加進 Dashboard、拖拉排版(Step 8) | report_dashboardcard |
哪張卡掛在哪個 Dashboard、位置座標(row/col)與尺寸(size_x/size_y)——編輯模式拖拉卡片,改的就是這幾個數字 |
| 每一次查詢執行 | query_execution |
誰、透過哪張卡、跑了幾毫秒。SQL 編輯器裡沒存檔就執行的查詢,card_id 是 NULL、context 是 ad-hoc——留紀錄、不留定義 |
| 建管理員帳號(Step 3)、收藏集(Step 5) | core_user、collection |
帳號與資料夾結構 |
💡 新版 Metabase 內建一組 E-commerce 範例內容(Examples 收藏集),所以
report_card裡會先有幾十筆不是你建的資料——用ORDER BY id DESC或名稱分辨自己的。
對照出完整的資料流向——打開一次 Dashboard,三個資料庫動作各自發生:
打開 Dashboard
│
├─► 讀 metabasedb:拿 Dashboard 與 Question 的定義(metabase_app)
├─► 查 mydb:即時執行查詢拿股價數據(metabase_ro,唯讀)→ 結果回傳瀏覽器畫圖,不落地
└─► 寫 metabasedb:留下 query_execution 執行紀錄(metabase_app)
mydb 全程只被 SELECT——「設定庫存定義與紀錄、資料來源只被讀」在這裡有完整的證據。
檢查你是不是真的做到了
| # | 你應該看到 | 它證明了什麼 |
|---|---|---|
| 1 | Metabase 能列出 mydb 的資料表 |
資料來源連線成功 |
| 2 | 三張圖各自做得出來 | 折線 / 長條 / 數字卡片三種基本圖 |
| 3 | Dashboard 同頁顯示三張圖 | 你能組報表,不只單圖 |
| 4 | SQL 模式查得到資料 | 點選式之外你還有 SQL 這條路 |
| 5 | VIEW 同步後能拿來做圖 | DB 端的查詢層可以被 BI 直接用 |
| 6 | 重啟 Metabase 容器後 Dashboard 還在 | 設定庫在 MySQL 裡,容器本身無狀態 |
| 7 | metabasedb 查得到你的 Dashboard、卡片與查詢紀錄 |
設定庫存的是定義與紀錄,內容隨操作累積 |
想再深入一點
- BI 工具把「查詢能力」下放給非技術同事。 有了 Metabase,原本要工程師寫 SQL 才做得到的「看某段期間某股走勢」,業務或主管自己拉一拉就有了。工程師不必逐一處理這類查詢需求。
- Metabase 的圖表底下其實還是 SQL。 你用滑鼠拉的每一個問題,Metabase 都會翻譯成一段 SQL 去查 MySQL。它也有「原生查詢」模式讓你直接寫 SQL。所以 BI 不是取代 SQL,而是幫你把常用查詢包成好點的介面。Step 11 查
report_card的dataset_query欄位就能看到這件事的實體:點選式的問題存成 MBQL 中間格式(執行時才翻譯成 SQL),SQL 模式的問題存的就是你寫的原文。 - Metabase 容器是無狀態的。 設定全部存在 MySQL 的
metabasedb,容器裡沒有任何需要保留的資料——刪掉 Metabase 容器再重建,只要MB_DB_*指回同一個設定庫,Dashboard 原封不動。反過來說,設定的存亡跟著 MySQL 的 volume:對 MySQL 用down -v,股價資料和 Dashboard 會一起消失。「狀態集中到資料庫、應用程式本身無狀態」是伺服器應用的常見設計,方便隨時汰換或擴充應用容器。
想一想(確認你懂了)
Q1:Metabase 存 Dashboard 的地方,跟它查詢的股價資料,是同一個資料庫嗎?
在同一台 MySQL 伺服器上,但是兩個不同的 database:Dashboard、帳號這些「Metabase 自己的設定」存在 metabasedb(Metabase 用 metabase_app 讀寫);股價資料在 mydb(Metabase 用 metabase_ro 唯讀查詢)。兩者各自獨立——清空 mydb 不會弄壞 Dashboard 定義,只是圖表查不到數據。
Q2:為什麼連線 Host 要填 mysql 而不是 127.0.0.1?
因為 Metabase 和 MySQL 是兩個 Docker 容器。在 Docker 網路裡,容器之間要用「服務名」互相找,mysql 這個名字會被解析到 MySQL 容器。如果填 127.0.0.1,那指的是 Metabase 容器自己,裡面根本沒有 MySQL,就連不到。
Q3:有了 BI 工具,哪些原本要工程師做的事可以下放給別人?
各種「看數據」的需求:某段期間的股價走勢、比較幾支股票、算平均、做月報表……這些原本要工程師寫 SQL 撈,現在非技術同事在 Metabase 上點一點就有,還能存成 Dashboard 重複看。工程師就能把時間留給更難的事。
Q4:什麼時候用點選式、什麼時候切到 SQL 模式?
日常的篩選、彙總、分組,點選式就夠、而且做出來的問題別人容易改。遇到複雜邏輯(多表 JOIN、視窗函數、CASE WHEN)點選式做不出來,就切 SQL 模式。實務上兩者混用:SQL 高手寫好複雜查詢存起來,其他人拿去改篩選條件。
Q5:metabase_app 和 metabase_ro 可以合併成一個帳號嗎?
功能上可行(一個帳號同時給 metabasedb 讀寫和 mydb 唯讀,甚至直接用 root),但兩個帳號各自對應一種需求:設定庫要能建表寫入、資料來源只需要讀。合併成高權限帳號後,BI 對業務資料就有了寫入能力——Step 3.5 用權限層擋下 DELETE 的保護跟著消失。分開是最小權限原則的實際應用:每個連線只拿它需要的權限。
換你試試看
練習 1:比較多支股票
做一張圖,同時畫 2330 和 2317 的收盤價走勢(用 stock_id 當分組)。你會得到兩條線疊在一起,可以直觀比較兩支股票的走勢。這讓你熟悉 Metabase 的「分組」功能。
練習 2:算一個彙總數字
做一個問題:查某支股票在某個月的「平均收盤價」或「最高成交量」。這讓你體會 BI 不只畫線圖,也能做彙總統計(相當於 SQL 的 AVG、MAX、GROUP BY),而你完全不用寫 SQL。
練習 3:驗證持久化
做好一張 Dashboard 後,把 Metabase 容器整個刪掉再重建(docker compose -f metabase/docker-compose-metabase.yml down 再 up -d),重新登入看 Dashboard 還在不在。它應該還在——設定存在 MySQL 的 metabasedb,不在容器裡,所以連 down 都不會弄丟它。
練習 4:把 Dashboard 加上第四張圖
用 SQL 模式寫一個「每日總成交量 Top 5」的查詢(提示:GROUP BY date ORDER BY SUM(Trading_Volume) DESC LIMIT 5),做成長條圖加進 Dashboard。這讓你練習「SQL 查詢 → 圖表 → Dashboard」的完整路徑。
卡住了?常見錯誤這樣排
| 你遇到的狀況 | 原因 | 怎麼解 |
|---|---|---|
Metabase 容器起不來(Exited 或反覆重啟) |
開機連不到設定庫:MySQL 沒啟動、沒接 my_network,或 metabasedb/metabase_app 沒建 |
照 Step 2 的順序重跑四個指令;docker logs metabase 看錯誤訊息 |
| 連 MySQL 一直失敗 | Host 填了 127.0.0.1,或兩容器不同網路 |
改填服務名 mysql;跑 docker network connect my_network mysql |
| http://localhost:3000 一片空白 | Metabase(JVM)還在啟動 | 等啟動完成再刷新,或用 Step 2 的等待迴圈 |
| 看不到任何資料表 | MySQL 裡 mydb 沒資料,或帳密錯 |
回 Step 1 載入 mock 資料;確認 metabase_ro/metabase |
| 新增資料庫時帳密被拒 | metabase_ro 還沒建,或密碼打錯 |
回 Step 3.5 建帳號;SHOW GRANTS FOR 'metabase_ro'@'%' 檢查 |
| 新建的 VIEW 在 Metabase 找不到 | schema 還沒同步 | 管理員 → 資料庫 → 同步資料庫 schema |
| 重啟後 Dashboard 不見 | 連到了別台 MySQL,或 metabasedb 被刪 |
確認 MB_DB_* 指向同一台 MySQL;別對 MySQL 的 compose 用 down -v |
收工
docker compose -f metabase/docker-compose-metabase.yml down # 設定都在 MySQL 的 metabasedb,容器移除不影響
要注意的是 MySQL 那邊:對
docker-compose-local.yml用down -v會把股價資料和 Metabase 設定一起刪掉,要整個重來才用。
這一章你學到了
- BI 工具是資料的出口,讓資料變成可讀的圖表與決策依據。
- 三種基本圖表 + Dashboard 組合 + SQL 模式 + VIEW,就能涵蓋大部分報表需求。
- Metabase 的設定庫(
metabasedb)和資料來源(mydb)是同一台 MySQL 裡的兩個 database,連線帳號與權限各自獨立。 - Docker 網路內容器互連要用服務名(
mysql),不是 localhost。
下一章要做什麼
Dashboard 有了,但資料更新目前仍靠手動跑 producer。下一章用 APScheduler 定時觸發爬蟲,讓整條 pipeline 自動執行。

送件鴿飛出去了