🧚 お知らせ!Beekeeper Studio は高速でモダンなオープンソースのデータベース GUI です ダウンロード
2026年1月7日 著者: Matthew Rathbone

ピボットテーブルは行を列に変換します。グループごとに1行、別のフィールドの値ごとに1列(たとえば四半期ごとに1列)という形です。SQL では、SUM などの集計関数の中で CASE を使い、各行の単位となる項目でグループ化して作成します。例は PostgreSQL、MySQL、SQLite、SQL Server で動作します。

SQL でピボットテーブルを作る方法

出力する列ごとに集計を1つずつ書きます。その中の CASE がその列に属する行だけを通し、GROUP BY でグループごとに1行にまとめます。

SELECT
  row_column,
  SUM(CASE WHEN pivot_column = 'value 1' THEN amount ELSE 0 END) AS value_1,
  SUM(CASE WHEN pivot_column = 'value 2' THEN amount ELSE 0 END) AS value_2
FROM table_name
GROUP BY row_column;

ここでは各製品が1行になり、四半期ごとの数量が横に並びます。

-- Input: sales
-- product  | quarter | units
-- ---------+---------+------
-- Keyboard | Q1      | 120
-- Keyboard | Q2      | 95
-- Monitor  | Q1      | 40
-- Monitor  | Q2      | 55
-- Mouse    | Q2      | 210

SELECT
  product,
  SUM(CASE WHEN quarter = 'Q1' THEN units ELSE 0 END) AS q1,
  SUM(CASE WHEN quarter = 'Q2' THEN units ELSE 0 END) AS q2
FROM sales
GROUP BY product
ORDER BY product;

-- Output:
-- product  | q1  | q2
-- ---------+-----+----
-- Keyboard | 120 | 95
-- Monitor  | 40  | 55
-- Mouse    | 0   | 210

押さえておくべきルール

  • 列名はすべて自分で指定します。SQL はデータから列を作れないため、第3四半期を加えるには3行目の CASE が必要です。値の一覧が頻繁に変わる場合は、アプリケーションコード側でクエリを組み立ててください。
  • ELSE 0 は SUM とカウント専用です。存在しない組み合わせを NULL ではなく 0 にします。AVG、MIN、MAX では ELSE を省いてください。そうしないと 0 が実際の値として扱われます。
-- Input: the sales table above

SELECT
  product,
  ROUND(AVG(CASE WHEN quarter = 'Q1' THEN units ELSE 0 END), 1) AS q1_else_0,
  ROUND(AVG(CASE WHEN quarter = 'Q1' THEN units END), 1) AS q1_no_else
FROM sales
GROUP BY product
ORDER BY product;

-- Output:
-- product  | q1_else_0 | q1_no_else
-- ---------+-----------+-----------
-- Keyboard | 60.0      | 120.0
-- Monitor  | 20.0      | 40.0
-- Mouse    | 0.0       | NULL

別の構文

PostgreSQL と SQLite の FILTER や、SQL Server の PIVOT 演算子を見かけることもあります。どちらも存在しない組み合わせは 0 ではなく NULL のままになります。

-- Input: the sales table above

-- PostgreSQL and SQLite
SELECT
  product,
  SUM(units) FILTER (WHERE quarter = 'Q1') AS q1,
  SUM(units) FILTER (WHERE quarter = 'Q2') AS q2
FROM sales
GROUP BY product
ORDER BY product;

-- SQL Server
SELECT product, [Q1] AS q1, [Q2] AS q2
FROM (SELECT product, quarter, units FROM sales) AS src
PIVOT (SUM(units) FOR quarter IN ([Q1], [Q2])) AS p
ORDER BY product;

-- Output:
-- product  | q1   | q2
-- ---------+------+----
-- Keyboard | 120  | 95
-- Monitor  | 40   | 55
-- Mouse    | NULL | 210

MySQL のコードでは、CASE 版を短く書いた SUM(IF(quarter = 'Q1', units, 0)) をよく見かけます。PostgreSQL の tablefunc 拡張には crosstab() 関数もあります。詳しくはPostgreSQL で crosstab() を使ってピボットテーブルを作るをご覧ください。

ピボットテーブルはいつ使うべき?

行の一覧を表形式にしたほうが読みやすいときにピボットします。よくある理由は次のとおりです。

1つの指標を横並びで比較する

比較したい値(日、四半期、地域など)を長い一覧に散らばらせず、1行の中に並べます。

列ごとに複数の指標を表示する

出力列はそれぞれ独立した集計なので、組み合わせて表示できます。デバイスごとのセッション数とページビュー、チャネルごとの注文数と売上などです。

カテゴリごとに行数を数える

合計ではなく件数を数えるには、一致する行ごとに 1 を足すか、COUNT(CASE WHEN ... THEN 1 END) を使います。COUNT は他の行が生む NULL を無視します。ステータス別のタスク数や優先度別のチケット数はこうして求めます。

キーと値の行を列に変換する

設定や製品属性は、名前と値ごとに1行で保存されることがよくあります。MAX(CASE ...) でピボットすると、項目ごとに1行、属性ごとに1列になります。MAX はテキストにも使え、各グループ内の唯一の値を取り出します。

SQL ピボットテーブルの実例

プラットフォーム別のアプリ平均評価

平均を求めるときは ELSE を省きます。そうすれば、あるプラットフォームでレビューのないアプリは誤解を招くスコアではなく NULL になります。

-- Input: reviews
-- app      | platform | rating
-- ---------+----------+-------
-- Budgetly | iOS      | 5
-- Budgetly | iOS      | 4
-- Budgetly | Android  | 3
-- FitTrack | Android  | 4
-- FitTrack | Android  | 5

-- In SQL Server, AVG of an integer column drops the decimals: use rating * 1.0
SELECT
  app,
  ROUND(AVG(CASE WHEN platform = 'iOS' THEN rating END), 1) AS ios,
  ROUND(AVG(CASE WHEN platform = 'Android' THEN rating END), 1) AS android
FROM reviews
GROUP BY app
ORDER BY app;

-- Output:
-- app      | ios  | android
-- ---------+------+--------
-- Budgetly | 4.5  | 3.0
-- FitTrack | NULL | 4.5

地域ごとのチャネル別注文数と売上

チャネルごとに注文数と合計の2つの指標を出します。

-- Input: orders
-- id | region | channel | amount
-- ---+--------+---------+-------
-- 1  | North  | web     | 120.00
-- 2  | North  | store   | 80.00
-- 3  | North  | web     | 45.50
-- 4  | South  | store   | 210.00
-- 5  | South  | store   | 64.00

SELECT
  region,
  COUNT(CASE WHEN channel = 'web' THEN 1 END) AS web_orders,
  SUM(CASE WHEN channel = 'web' THEN amount ELSE 0 END) AS web_sales,
  COUNT(CASE WHEN channel = 'store' THEN 1 END) AS store_orders,
  SUM(CASE WHEN channel = 'store' THEN amount ELSE 0 END) AS store_sales
FROM orders
GROUP BY region
ORDER BY region;

-- Output:
-- region | web_orders | web_sales | store_orders | store_sales
-- -------+------------+-----------+--------------+------------
-- North  | 2          | 165.50    | 1            | 80.00
-- South  | 0          | 0         | 2            | 274.00

担当者ごとの優先度別サポートチケット数

優先度ごとの件数と合計です。COUNT(*) は引き続きグループ内のすべての行を数えます。

-- Input: tickets
-- id | agent | priority
-- ---+-------+---------
-- 1  | Priya | urgent
-- 2  | Priya | normal
-- 3  | Marco | normal
-- 4  | Priya | normal
-- 5  | Marco | low

SELECT
  agent,
  COUNT(CASE WHEN priority = 'low' THEN 1 END) AS low,
  COUNT(CASE WHEN priority = 'normal' THEN 1 END) AS normal,
  COUNT(CASE WHEN priority = 'urgent' THEN 1 END) AS urgent,
  COUNT(*) AS total
FROM tickets
GROUP BY agent
ORDER BY agent;

-- Output:
-- agent | low | normal | urgent | total
-- ------+-----+--------+--------+------
-- Marco | 1   | 1      | 0      | 2
-- Priya | 0   | 2      | 1      | 3

ユーザーごとに1行の設定

各設定は名前と値で保存されています。設定していないユーザーはその列が NULL になります。

-- Input: user_settings
-- user_id | setting  | value
-- --------+----------+--------------
-- 1       | theme    | dark
-- 1       | language | en
-- 2       | language | de
-- 2       | timezone | Europe/Berlin
-- 3       | theme    | light

SELECT
  user_id,
  MAX(CASE WHEN setting = 'theme' THEN value END) AS theme,
  MAX(CASE WHEN setting = 'language' THEN value END) AS language,
  MAX(CASE WHEN setting = 'timezone' THEN value END) AS timezone
FROM user_settings
GROUP BY user_id
ORDER BY user_id;

-- Output:
-- user_id | theme | language | timezone
-- --------+-------+----------+--------------
-- 1       | dark  | en       | NULL
-- 2       | NULL  | de       | Europe/Berlin
-- 3       | light | NULL     | NULL

ピボットテーブルについてさらに学ぶ

このブログの記事:

公式ドキュメント:

Beekeeper Studioは無料のオープンソースのデータベースGUIです

これまで使った中で最高のSQLクエリ&エディタツールです。データベースの管理に必要なものがすべて揃っています。 - ⭐⭐⭐⭐⭐ Mit

Beekeeper Studioは高速で直感的、そして使いやすいツールです。多数のデータベースに対応し、Windows、Mac、Linuxで快適に動作します。

BeekeeperのLinux版は100%フル機能。手抜きも機能の妥協も一切ありません。

Beekeeper Studio 利用者の声

★★★★★
「Beekeeper Studio で以前の SQL ワークフローを完全に置き換えました。高速で直感的、データベース作業がまた楽しくなりました。」
— Alex K.、データベース開発者
★★★★★
「多くのデータベース GUI を試しましたが、Beekeeper は機能とシンプルさのバランスが完璧です。とにかくちゃんと動きます。」
— Sarah M.、フルスタックエンジニア