はじめに
計測データを扱っていると、必ず一度は悩みます。
「データって、縦持ちと横持ちどっちがいいんだろう?」
- UNION で View を作るべきか
- 粒度違いのデータをどう扱うか
- 指標が増えたらどうなるか
こうした悩みの多くは、
縦持ち / 横持ちを「どちらかに決めよう」とすることから生まれます。
本記事では、
- 縦持ち・横持ちの違い
- それぞれのメリット・デメリット
- 計測データでの現実的な使い分け
を整理します。
結論
保存は縦持ち、利用は横持ち
これが、計測データ設計で最も事故が少ない考え方です。
縦持ちとは何か
典型的な構造(ロング形式)
fact_measurement
-----------------------------
timestamp
metric_type -- temperature / humidity / pressure ...
value
interval_minutes
aggregation_type
source
1行が表すのは、
「いつ・何を・いくつ」
という 計測の事実 です。
縦持ちのメリット
① 計測データの本質に合っている
- 指標が増えてもスキーマ変更不要
- NULL 列が増えない
INSERT INTO fact_measurement
(timestamp, metric_type, value)
VALUES ('10:00', 'co2', 800);
② 粒度・集計違いを安全に扱える
- 5分 / 1時間
- raw / avg / max
といった 意味の違いを列で表現 できるため、
- 生データと集計データが混ざる事故
- UNION 時の意味破綻
を防ぎやすい。
③ 将来拡張に強い
- 新指標
- 新粒度
- 新データソース
すべて 行追加 で対応できる。
縦持ちのデメリット
❌ クエリが読みにくい
SELECT
MAX(CASE WHEN metric_type = 'temperature' THEN value END) AS temperature,
MAX(CASE WHEN metric_type = 'humidity' THEN value END) AS humidity
FROM fact_measurement
GROUP BY timestamp;
- CASE が増える
- 初見で意図が読みづらい
❌ 表示・帳票向きではない
- CSV
- グラフ
- BI ツール
は横持ち前提が多い。
横持ちとは何か
典型的な構造(ワイド形式)
measurement_hourly
-----------------------------
timestamp
temperature
humidity
pressure
1行が表すのは、
「ある時点の状態」
です。
横持ちのメリット
① クエリが直感的
SELECT timestamp, temperature, humidity
FROM measurement_hourly;
- 読みやすい
- アプリコードが素直
② 表示・集計に強い
- ダッシュボード
- レポート
- 画面表示
横持ちのデメリット
❌ 拡張に弱い
- 指標追加 = ALTER TABLE
- NULL 列だらけ
❌ 意味の違いを混ぜやすい
temperatureが
- 実測
- 平均
- 最大
のどれか分からなくなる。
計測データでの現実的な使い分け
| 目的 | 向いている形式 |
|---|---|
| 事実の保存 | 縦持ち |
| 生データ管理 | 縦持ち |
| 粒度違い統合 | 縦持ち |
| 参照・集計 | 横持ち(View) |
| 表示・帳票 | 横持ち |
おすすめ構成(実践例)
① 事実テーブル(縦持ち)
fact_measurement
-----------------------------
timestamp
metric_type
value
interval_minutes
aggregation_type
② 利用者向け View(横持ち)
CREATE VIEW v_measurement_hourly AS
SELECT
timestamp,
MAX(CASE WHEN metric_type = 'temperature' THEN value END) AS temperature,
MAX(CASE WHEN metric_type = 'humidity' THEN value END) AS humidity
FROM fact_measurement
WHERE interval_minutes = 60
AND aggregation_type = 'avg'
GROUP BY timestamp;
UNION / VIEW との関係
- 縦持ち:UNION しやすい(意味を保てる)
- 横持ち:UNION は View 側で限定的に使う
👉 UNION は「横持ちを作るための手段」のように考えると整理しやすい。
アンチパターン:縦持ち・横持ちでよくある失敗例
アンチパターン①:最初から横持ちで保存してしまう
measurement
-----------------------------
timestamp
temperature
humidity
pressure
何が起きるか
- 指標追加のたびに ALTER TABLE
- NULL 列が増殖
- 生データ/集計データが同居し始める
👉 「今は楽、将来が地獄」 な典型例
アンチパターン②:縦持ちをそのままアプリに触らせる
fact_measurement
-----------------------------
timestamp
metric_type
value
何が起きるか
- 画面ごとに CASE 文量産
- クエリの意図が読めない
- バグ修正が怖い
👉 縦持ちは内部構造、外に出さない
アンチパターン③:意味の違うデータを縦持ちで混在させる
fact_measurement
-----------------------------
timestamp
metric_type
value
temperature = 実測 / 平均 / 最大
何が起きるか
- 同じ metric_type なのに意味が違う
- 集計すると意味破綻
👉 aggregation_type を持たない縦持ちは危険
アンチパターン④:万能な横持ち VIEW を作る
v_measurement_all
= 生データ + 時間集計 + 日次 + 月次
何が起きるか
- VIEW が巨大化
- パフォーマンス劣化
- 利用者が前提を理解できない
👉 横持ち VIEW は用途限定が基本
アンチパターン⑤:縦持ち・横持ちを二者択一で考える
「縦持ちは正規化されていて正しい」
「横持ちはアンチパターン」
何が問題か
- レイヤーの概念が消える
- 設計議論が宗教戦争になる
👉 役割が違うだけで、優劣ではない
まとめ
- 縦持ち・横持ちは対立概念ではない
- 役割が違うだけ
縦持ち:事実に強い
横持ち:人間に優しい
計測データでは、
この2つを レイヤーで分ける
ことが重要です。
おわりに
縦持ち / 横持ちの話は、
- UNION + VIEW
- Read Model / Write Model
- 集計データの扱い
と強くつながっています。
これらを一緒に考えると、
データ設計で迷うポイントがかなり減ります。