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

サブクエリとは、別のクエリの中に入れ子になったクエリです。CTE(共通テーブル式)は、同じ内側のクエリを WITH で文の先頭に移動して名前を付けたもので、長いクエリを一連のステップとして読めるようになります。例は PostgreSQL、MySQL、SQLite、SQL Server で動作します。

CTE とサブクエリの使い方

サブクエリは使う場所にそのまま書きます。FROM ではテーブルとして働き、WHERE や SELECT では値やリストを返します。CTE は先に定義し、その後に続くクエリからテーブルのように使います:

-- Subquery: written where it's used
SELECT columns FROM (SELECT ...) AS name;

-- CTE: named first, then used like a table
WITH name AS (SELECT ...)
SELECT columns FROM name;
SELECT n * 2 AS doubled FROM (SELECT 21 AS n) AS numbers;
-- Output: 42

WITH numbers AS (SELECT 21 AS n)
SELECT n * 2 AS doubled FROM numbers;
-- Output: 42

実際のテーブルでは、どちらの形式でも同じ行が返ります。同じフィルターを両方の書き方で示します:

-- Input: people
-- first_name | last_name | job      | favorite_food
-- -----------+-----------+----------+--------------
-- Matthew    | Ortiz     | Chef     | cheese
-- Matt       | Novak     | Pilot    | pizza
-- Mattie     | Brooks    | Engineer | cheese
-- Priya      | Nair      | Teacher  | cheese

-- Subquery
SELECT *
FROM (SELECT first_name, last_name, job FROM people WHERE favorite_food = 'cheese') AS cheese_lovers
WHERE first_name LIKE 'Matt%';

-- CTE
WITH cheese_lovers AS (
  SELECT first_name, last_name, job FROM people WHERE favorite_food = 'cheese'
)
SELECT * FROM cheese_lovers
WHERE first_name LIKE 'Matt%';

-- Output of both:
-- first_name | last_name | job
-- -----------+-----------+---------
-- Matthew    | Ortiz     | Chef
-- Mattie     | Brooks    | Engineer

知っておくべきルール

  • CTE は 1 つの文の中でしか有効ではありません。WITH の直後のクエリでは使えますが、次の文では使えません。複数のクエリで結果を保持したい場合は、一時テーブルかビューを使ってください。
  • FROM 内のサブクエリには名前を付けてください。上の AS numbers のようにします。名前がないと MySQL と SQL Server ではエラーになります。
  • 単一の値として使うサブクエリは 1 行だけを返す必要があります:
SELECT (SELECT 1 UNION ALL SELECT 2) AS n;
-- ERROR: more than one row returned by a subquery used as an expression
-- (SQLite doesn't complain: it quietly uses the first row, 1)
  • 速度は通常同じです。一般的にデータベースは、1 回だけ使われる CTE を同等のサブクエリと同じように実行計画を立てるため、読みやすい方を選んでください。

別の構文

自分自身を参照する CTE(後述)は WITH RECURSIVE と書きます:

-- In SQL Server, leave out RECURSIVE
WITH RECURSIVE counter AS (
  SELECT 1 AS n
  UNION ALL
  SELECT n + 1 FROM counter WHERE n < 3
)
SELECT n FROM counter;

-- Output:
-- n
-- -
-- 1
-- 2
-- 3

SQL Server では、WITH の前の文はセミコロンで終わる必要があります。そのため T-SQL のコードでは CTE を ;WITH で始めることがよくあります。

サブクエリではなく CTE を使うべきなのはいつ?

小さなステップが 1 つだけならサブクエリで十分です。クエリに複数のステップがある場合、同じ結果を 2 回使う場合、または自分自身を参照する必要がある場合は CTE に切り替えましょう。

計算した 1 つの値でフィルターする

内側のクエリが、注文合計の平均のような比較用の値やリストを返すだけなら、WHERE 内のサブクエリが最も短く書けます。CTE でも動きますが、不要な名前が増えるだけです。

長いクエリを名前付きのステップに分ける

クエリに段階がある場合(顧客ごとの合計、次に上位数件、最後に最終リスト)は、各段階に名前を付けましょう。1 つの WITH の後に複数の CTE をカンマ区切りで並べると、それぞれが前の CTE を使えるため、クエリを内側から外側ではなく上から下へ読めるようになります。

同じ結果を複数回使う

1 つの文の中で CTE は何度でも参照できるため、2 回必要な結果(合計のリストとその平均、各合計と総合計など)を 1 回書くだけで済みます。サブクエリでは内側のクエリ全体をコピーすることになります。

階層をたどる

自分自身を参照できるのは CTE だけです。再帰 CTE はいくつかの行から始まり、新しい行が見つからなくなるまでテーブルとの結合を繰り返します。これにより組織図やカテゴリツリーをたどったり、連続した数値や日付を生成したりできます。

CTE とサブクエリの実例

カテゴリ平均より高い価格の商品

このサブクエリは外側のクエリの p.category を参照するため、各商品が属するカテゴリごとに計算されます(相関サブクエリ):

-- Input: products
-- name          | category  | price
-- --------------+-----------+-------
-- Desk Lamp     | Lighting  | 24.99
-- Floor Lamp    | Lighting  | 89.00
-- Office Chair  | Furniture | 149.00
-- Standing Desk | Furniture | 399.00
-- Bookshelf     | Furniture | 120.00

SELECT name, category, price
FROM products p
WHERE price > (
  SELECT AVG(price) FROM products WHERE category = p.category
)
ORDER BY category, name;

-- Output:
-- name          | category  | price
-- --------------+-----------+-------
-- Standing Desk | Furniture | 399.00
-- Floor Lamp    | Lighting  | 89.00

カテゴリごとの売れ筋商品

名前付きの 3 つのステップで構成します。商品ごとの販売数を合計し、カテゴリ内で商品を順位付けし、最上位の商品だけを残します:

-- Input: sales
-- id | product      | category  | units
-- ---+--------------+-----------+------
-- 1  | Desk Lamp    | Lighting  | 3
-- 2  | Floor Lamp   | Lighting  | 5
-- 3  | Desk Lamp    | Lighting  | 4
-- 4  | Office Chair | Furniture | 2
-- 5  | Bookshelf    | Furniture | 6

WITH product_totals AS (
  SELECT category, product, SUM(units) AS units
  FROM sales
  GROUP BY category, product
),
ranked AS (
  SELECT category, product, units,
         RANK() OVER (PARTITION BY category ORDER BY units DESC) AS place
  FROM product_totals
)
SELECT category, product, units
FROM ranked
WHERE place = 1
ORDER BY category;

-- Output:
-- category  | product   | units
-- ----------+-----------+------
-- Furniture | Bookshelf | 6
-- Lighting  | Desk Lamp | 7

地域ごとの売上シェア

region_totals は 2 回使われます。1 回は行の取得に、もう 1 回は総合計を求めるサブクエリの中で使われます:

-- Input: orders
-- id | region | amount
-- ---+--------+--------
-- 1  | North  | 1200.00
-- 2  | South  | 450.00
-- 3  | North  | 300.00
-- 4  | West   | 800.00
-- 5  | South  | 250.00

WITH region_totals AS (
  SELECT region, SUM(amount) AS total
  FROM orders
  GROUP BY region
)
SELECT
  region,
  total,
  ROUND(100.0 * total / (SELECT SUM(total) FROM region_totals), 1) AS pct_of_sales
FROM region_totals
ORDER BY total DESC;

-- Output:
-- region | total   | pct_of_sales
-- -------+---------+-------------
-- North  | 1500.00 | 50.0
-- West   | 800.00  | 26.7
-- South  | 700.00  | 23.3

サブクエリで書くと、GROUP BY のクエリが 2 回登場することになります。

従業員の上司の系列

1 人の従業員から始めて、manager_id がなくなるまで上へたどります:

-- Input: employees
-- id | name      | manager_id
-- ---+-----------+-----------
-- 1  | Maya Chen | NULL
-- 2  | Sam Ortiz | 1
-- 3  | Jo Kim    | 2
-- 4  | Lee Ross  | 2
-- 5  | Ana Diaz  | 1

-- In SQL Server, leave out RECURSIVE
WITH RECURSIVE chain AS (
  SELECT id, name, manager_id, 0 AS level
  FROM employees
  WHERE name = 'Jo Kim'
  UNION ALL
  SELECT e.id, e.name, e.manager_id, chain.level + 1
  FROM employees e
  JOIN chain ON e.id = chain.manager_id
)
SELECT level, name FROM chain ORDER BY level;

-- Output:
-- level | name
-- ------+----------
-- 0     | Jo Kim
-- 1     | Sam Ortiz
-- 2     | Maya Chen

CTE とサブクエリについての関連情報

このブログの記事:

公式ドキュメント:

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

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

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

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

Beekeeper Studio 利用者の声

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