ページの作成
親となるページを選択してください。
親ページに紐づくページを子ページといいます。
例: 親=スポーツ, 子1=サッカー, 子2=野球
子ページを親ページとして更に子ページを作成することも可能です。
例: 親=サッカー, 子=サッカーのルール
親ページはいつでも変更することが可能なのでとりあえず作ってみましょう!
| この記事の要点 |
|
インラインビューとは
SQL の FROM 句にサブクエリを書き、その結果を仮想テーブルのように扱う技法をインラインビュー (Inline View) と呼びます。「インライン (in-line) = 行内」の意味で、永続的な VIEW オブジェクトを作らずクエリ内に埋め込みます。
-- 基本構文
SELECT *
FROM (
SELECT user_id, COUNT(*) AS order_count
FROM orders
WHERE created_at >= '2026-01-01'
GROUP BY user_id
) AS recent_orders -- ★ エイリアス必須
WHERE order_count >= 10;
VIEW (永続ビュー) との違い
| 項目 | VIEW (永続ビュー) | インラインビュー |
|---|---|---|
| 定義場所 | DB に CREATE VIEW | ★ クエリ内 (FROM 句) |
| 名前 | あり (再利用可能) | そのクエリ限り |
| 永続化 | 定義は永続 | 都度生成・破棄 |
| 権限管理 | VIEW にGRANT可能 | 元テーブル権限 |
| 用途 | 共通の集計・絞り込みを再利用 | その場の中間結果 |
-- 永続 VIEW
CREATE VIEW recent_orders AS
SELECT user_id, COUNT(*) AS order_count
FROM orders
WHERE created_at >= '2026-01-01'
GROUP BY user_id;
-- どこからでも使える
SELECT * FROM recent_orders WHERE order_count >= 10;
-- 一方インラインビューは 1 クエリ限り
SELECT *
FROM (SELECT user_id, COUNT(*) AS order_count
FROM orders WHERE ... GROUP BY user_id) AS r
WHERE r.order_count >= 10;
典型ユースケース 1: 集計結果を更に絞り込む
SQL では集計関数の結果に対して直接 WHERE は使えず HAVING が必要ですが、インラインビューで集計後の絞り込みを分かりやすく書けます。
-- HAVING でも書ける
SELECT user_id, COUNT(*) AS cnt
FROM orders
GROUP BY user_id
HAVING COUNT(*) >= 10;
-- インラインビューで段階的に
SELECT *
FROM (
SELECT user_id, COUNT(*) AS cnt
FROM orders
GROUP BY user_id
) AS t
WHERE cnt >= 10;
-- → t.cnt のエイリアスを再利用できる
典型ユースケース 2: TOP N (上位 N 件)
-- カテゴリごとに売上 TOP 3 (ROW_NUMBER + インラインビュー)
SELECT category_id, product_name, sales
FROM (
SELECT
category_id,
product_name,
sales,
ROW_NUMBER() OVER (
PARTITION BY category_id
ORDER BY sales DESC
) AS rn
FROM products
) AS ranked
WHERE rn <= 3;
-- 国別の人口最大都市
SELECT country, city, population
FROM (
SELECT
country,
city,
population,
ROW_NUMBER() OVER (PARTITION BY country ORDER BY population DESC) AS rn
FROM cities
) AS t
WHERE rn = 1;
典型ユースケース 3: 結合前に絞り込み
-- ❌ 全件 JOIN してから絞り込み (重い)
SELECT u.name, o.total
FROM users u
JOIN orders o ON u.id = o.user_id
WHERE o.created_at >= '2026-01-01';
-- ✅ インラインビューで先に絞り込む
SELECT u.name, ro.total
FROM users u
JOIN (
SELECT user_id, SUM(amount) AS total
FROM orders
WHERE created_at >= '2026-01-01'
GROUP BY user_id
) AS ro ON u.id = ro.user_id;
-- → DB によっては自動最適化されるが、明示的に書くと安定
相関サブクエリとの違い
| 種類 | 場所 | 外側参照 | 例 |
|---|---|---|---|
| スカラサブクエリ | SELECT / WHERE | 可 | SELECT (SELECT MAX(...) FROM ...) |
| インラインビュー | ★ FROM | 不可 (LATERAL 除く) | FROM (SELECT ...) AS t |
| 相関サブクエリ | WHERE / SELECT | ★ する | WHERE EXISTS (SELECT 1 FROM ... WHERE x = outer.x) |
CTE (WITH) | クエリ先頭 | 不可 | WITH x AS (...) SELECT ... |
CTE (Common Table Expression / WITH 句)
インラインビューが深くネストすると読みづらいので、CTE で名前を付けて先頭に分離する方が読みやすくなります。
-- ❌ インラインビューが深い
SELECT *
FROM (
SELECT category_id, AVG(price) AS avg_price
FROM (
SELECT category_id, price
FROM products
WHERE deleted_at IS NULL
) AS active_products
GROUP BY category_id
) AS category_avg
WHERE avg_price > 1000;
-- ✅ CTE で段階的に
WITH active_products AS (
SELECT category_id, price
FROM products
WHERE deleted_at IS NULL
),
category_avg AS (
SELECT category_id, AVG(price) AS avg_price
FROM active_products
GROUP BY category_id
)
SELECT * FROM category_avg WHERE avg_price > 1000;
-- 再帰 CTE で組織図のような階層を辿る
WITH RECURSIVE org AS (
SELECT id, name, parent_id, 0 AS level
FROM employees WHERE parent_id IS NULL
UNION ALL
SELECT e.id, e.name, e.parent_id, o.level + 1
FROM employees e
JOIN org o ON e.parent_id = o.id
)
SELECT * FROM org ORDER BY level, id;
各 DB の対応状況
| DB | インラインビュー | CTE | 再帰 CTE |
|---|---|---|---|
| MySQL 8.0+ | ○ | ○ | ○ |
| MySQL 5.7 以下 | ○ | × | × |
| MariaDB 10.2+ | ○ | ○ | ○ |
| PostgreSQL | ○ | ○ | ○ |
| Oracle | ○ | ○ | ○ |
| SQL Server | ○ | ○ | ○ |
| SQLite 3.8.3+ | ○ | ○ | ○ |
パフォーマンスの注意点
- 多くの DB はインラインビューをマージして最適化する (MySQL 5.7+, PostgreSQL, Oracle, SQL Server)
- ただし ORDER BY / LIMIT / GROUP BY を含むと materialize される傾向 (= テンポラリテーブル化)
- 大きな結果セットをインラインビューにするとメモリ・ディスク負荷
- 外側からインデックスが効かなくなることがある → EXPLAIN で確認
- PostgreSQL 12+ では CTE もマージされるようになった (古い PG は CTE が最適化フェンスだった)
-- 実行計画を確認
EXPLAIN
SELECT *
FROM (SELECT user_id, COUNT(*) AS cnt FROM orders GROUP BY user_id) AS t
WHERE cnt >= 10;
-- PostgreSQL の場合
EXPLAIN ANALYZE ...;
エイリアス必須に注意
-- ❌ MySQL / PostgreSQL: ERROR (エイリアスが無い)
SELECT * FROM (SELECT id FROM users);
-- ✅ AS で名前を付ける (AS は省略可だが付ける方が読みやすい)
SELECT * FROM (SELECT id FROM users) AS t;
-- インラインビュー内のカラム名が重複する場合は外側で参照不能
SELECT a, b
FROM (
SELECT u.id AS a, o.id AS a, ... -- ❌ 重複
FROM users u JOIN orders o ON ...
) AS t;
-- 明示的に別名を付ける
SELECT t.uid, t.oid
FROM (
SELECT u.id AS uid, o.id AS oid
FROM users u JOIN orders o ON u.id = o.user_id
) AS t;
UPDATE / DELETE での利用
-- PostgreSQL / SQL Server で UPDATE FROM
UPDATE orders o
SET status = 'flagged'
FROM (
SELECT user_id
FROM orders
GROUP BY user_id
HAVING SUM(amount) > 1000000
) AS big_spenders
WHERE o.user_id = big_spenders.user_id;
-- MySQL は UPDATE with JOIN
UPDATE orders o
JOIN (
SELECT user_id FROM orders GROUP BY user_id HAVING SUM(amount) > 1000000
) AS big ON o.user_id = big.user_id
SET o.status = 'flagged';
FAQ
Q: インラインビューと CTE、どちらを使うべき?
A: 1 度しか使わないならインラインビューでも CTE でもよいが、段階が深い・複数回参照するなら CTE が圧倒的に読みやすい。新規 SQL なら CTE 推奨。
Q: インラインビューでインデックスは効く?
A: 内側の元テーブルのインデックスは効きます。外側からインラインビュー結果に対する更なる検索は、Optimizer がマージできれば効くが、materialize されるとフルスキャンになることも。EXPLAIN で確認。
Q: ネストが深くて読めない
A: CTE に書き直すか、永続 VIEW として切り出すと劇的に読みやすくなります。SQL は左から右・上から下に読める形にするのが原則。
ページの作成
親となるページを選択してください。
親ページに紐づくページを子ページといいます。
例: 親=スポーツ, 子1=サッカー, 子2=野球
子ページを親ページとして更に子ページを作成することも可能です。
例: 親=サッカー, 子=サッカーのルール
親ページはいつでも変更することが可能なのでとりあえず作ってみましょう!
子ページはありません
人気ページ
- 1 Eclipseで「サーバーに追加または除去できるリソースがありません。」の原因と対処法
- 2 tomcat の起動 / 停止ログと catalina.log・catalina.out の違い
- 3 JavaScript base URL 取得方法|window.location.origin と SSR/Node.js 対応
- 4 YouTube Data API v3 エラー一覧|403/400/404 の主要原因と切り分け
- 5 Spring Frameworkのアノテーション一覧
- 6 Laravel エラー一覧|500/Blade/DB 接続/ルーティングの代表エラー
- 7 3Dグラフィックスとは|モデリング/レンダリング/主要ソフトウェア (Blender / Maya)
- 8 【Spring】@Valueアノテーションとは
- 9 CATALINA_HOME の確認方法 (Linux / Mac)
- 10 【Spring】@Autowiredアノテーションとは
最近更新/作成されたページ
- IPv6とは|128bitアドレス・コロン16進表記/::省略・リンクローカル・SLAAC・デュアルスタック NEW 2026-06-22 12:34:44
- VPNとは|暗号トンネル・サイト間/リモートアクセス・IPsec/SSL-VPN/WireGuardを解説 NEW 2026-06-22 12:19:10
- MAC アドレスフィルタリングの仕組みと限界 | ネットワーク入門 NEW 2026-06-22 12:19:10
- WebRTC とは ブラウザ間 P2P の音声・映像・データ通信 | ネットワーク入門 NEW 2026-06-22 12:17:25
- gRPC とは HTTP/2 + Protocol Buffers の高速 RPC | ネットワーク入門 NEW 2026-06-22 12:17:25
- HTTP/3 (QUIC) とは UDP ベースの低遅延 Web 通信 | ネットワーク入門 NEW 2026-06-22 12:17:25
- HTTP/2 とは 多重化・HPACK・バイナリフレーム | ネットワーク入門 NEW 2026-06-22 12:17:25
- Web通信プロトコル入門 HTTP/2・HTTP/3・WebSocket・gRPC・WebRTC | ネットワーク入門 NEW 2026-06-22 12:17:25
- WebSocket とは 全二重リアルタイム通信 ws/wss | ネットワーク入門 NEW 2026-06-22 12:17:25
- ファイアウォールとは|パケットフィルタ・ステートフル・DMZ・次世代FW(L4/L7)を解説 NEW 2026-06-22 12:17:24
- iptables/nftablesとは|テーブル・チェーン・ルール例・永続化をLinux視点で解説 NEW 2026-06-22 12:17:24
- HAProxy とは frontend/backend と設定例 | ネットワーク入門 NEW 2026-06-22 12:17:24
- 証明書と認証局(CA)とは|X.509・信頼チェーン・DV/OV/EV・失効(CRL/OCSP)を解説 NEW 2026-06-22 12:17:24
- CDN とは エッジキャッシュ・TTL・Cloudflare/CloudFront | ネットワーク入門 NEW 2026-06-22 12:17:24
- TLS/SSLの仕組み|ハンドシェイク・暗号スイート・前方秘匿性・証明書検証をわかりやすく解説 NEW 2026-06-22 12:17:24
コメントを削除してもよろしいでしょうか?