ビューとインデックス
ビューとインデックス
図書館で本を頻繁に借りる場面を想像してください。本を見つけるには2つの方法があります:1つ目は毎回すべての棚を探す方法(フルテーブルスキャン)、2つ目はまずインデックスカード目録を確認して正しい棚に直接行く方法(インデックスクエリ)。図書館の「おすすめ読書リスト」はビューのようなものです — 本物の棚ではありませんが、異なる棚から厳選された本のリストで、目的のものを見つけやすくします。SQLのビューとインデックスも同じように動作します。
1. コアコンセプト
ビュー — 概念
ビューは仮想テーブルで、データを保存しませんがSELECTクエリ文を保存します。ビューにアクセスするたびに、データベースはビューの定義されたクエリを実行し、結果を返します。
-- ビューを作成:従業員情報ビュー
CREATE VIEW v_employee_info AS
SELECT
e.employee_id,
CONCAT(e.first_name, e.last_name) AS name,
e.salary,
d.department_name AS department,
d.city
FROM employees e
LEFT JOIN departments d ON e.department_id = d.department_id;
ビューの使用は通常のテーブルと同じです:
-- ビューをクエリ
SELECT * FROM v_employee_info WHERE salary > 10000;
ビューの作成
CREATE VIEW view_name AS
SELECT statement;
-- 部門統計ビューを作成
CREATE VIEW v_department_stats AS
SELECT
d.department_id,
d.department_name AS department,
COUNT(e.employee_id) AS employee_count,
AVG(e.salary) AS avg_salary,
SUM(e.salary) AS total_salary
FROM departments d
LEFT JOIN employees e ON d.department_id = e.department_id
GROUP BY d.department_id, d.department_name;
ビューの変更
-- 方法1:CREATE OR REPLACE(推奨)
CREATE OR REPLACE VIEW v_employee_info AS
SELECT
e.employee_id,
CONCAT(e.first_name, e.last_name) AS name,
e.salary,
e.hire_date,
d.department_name AS department
FROM employees e
LEFT JOIN departments d ON e.department_id = d.department_id;
-- 方法2:ALTER VIEW
ALTER VIEW v_employee_info AS
SELECT employee_id, first_name, last_name, salary FROM employees;
ビューの削除
DROP VIEW IF EXISTS v_employee_info;
ビューの利点
| 利点 | 説明 |
|---|---|
| 複雑なクエリの簡素化 | 複雑なJOINをビューとしてカプセル化;SELECT * FROM view_nameで使用 |
| データセキュリティ | ユーザーに特定のカラムや行のみ公開し、機密データを非表示 |
| 論理的独立性 | ビュー定義の変更がビューを使用するアプリケーションに影響しない |
| 一貫したロジック | すべてのユーザーが同じクエリロジックを使用し、重複記述を回避 |
ビューの欠点
| 欠点 | 説明 |
|---|---|
| パフォーマンスオーバーヘッド | アクセスごとに基盤クエリを実行;複雑なビューは遅くなる可能性 |
| 更新制限 | JOIN、集約関数、DISTINCTを含むビューは通常更新不可 |
| デバッグの困難さ | 多層ネストされたビューはトラブルシューティングの困難さを増加 |
インデックス — 概念
インデックスはデータ構造で、本の目次に似ており、データベースがデータを素早く位置特定し、フルテーブルスキャンを回避するのに役立ちます。
-- インデックスなし:フルテーブルスキャン(行ごとに検索)
SELECT * FROM employees WHERE last_name = 'Zhang';
-- インデックスあり:直接ルックアップ(インデックス経由)
CREATE INDEX idx_last_name ON employees(last_name);
SELECT * FROM employees WHERE last_name = 'Zhang';
インデックスの作成
-- 通常インデックス(重複値を許可)
CREATE INDEX idx_column ON table_name(column_name);
-- ユニークインデックス(重複値を許可しない)
CREATE UNIQUE INDEX idx_column ON table_name(column_name);
-- フルテキストインデックス(全文検索用)
CREATE FULLTEXT INDEX idx_column ON table_name(column_name);
-- 複合インデックス(複数カラムの組み合わせ)
CREATE INDEX idx_columns ON table_name(column1, column2);
インデックスの削除
-- MySQL構文
DROP INDEX idx_column ON table_name;
-- 標準構文
ALTER TABLE table_name DROP INDEX idx_column;
インデックスの種類
| 種類 | キーワード | 説明 | ユースケース |
|---|---|---|---|
| 通常インデックス | INDEX |
基本インデックス、重複値を許可 | 頻繁にクエリされるカラム |
| ユニークインデックス | UNIQUE INDEX |
カラム値が一意である必要がある | メール、ID番号など |
| フルテキストインデックス | FULLTEXT INDEX |
全文検索をサポート | 記事コンテンツ、製品説明 |
| 複合インデックス | INDEX(col1, col2) |
複数カラムの組み合わせインデックス | 複数条件のクエリ |
複合インデックスと最左プレフィックスルール
複合インデックスは最左プレフィックスルールに従います:クエリ条件はインデックスの最も左のカラムから一致する必要があります。
-- 複合インデックスを作成
CREATE INDEX idx_name_age ON employees(last_name, first_name, salary);
-- ✅ インデックスを使用可能(最左カラムに一致)
SELECT * FROM employees WHERE last_name = 'Zhang';
SELECT * FROM employees WHERE last_name = 'Zhang' AND first_name = 'Wei';
-- ❌ インデックスを使用不可(最左カラムをスキップ)
SELECT * FROM employees WHERE first_name = 'Wei';
SELECT * FROM employees WHERE salary > 10000;
インデックスのコスト
| コスト | 説明 |
|---|---|
| ストレージスペース | インデックスには追加のディスクスペースが必要 |
| 書き込みパフォーマンス | INSERT/UPDATE/DELETEはインデックスの更新を同期する必要がある |
| メンテナンスコスト | インデックスが多すぎると書き込み速度が低下 |
2. 基本構文
ビューの構文
-- ビューを作成
CREATE [OR REPLACE] VIEW view_name AS SELECT statement;
-- ビューをクエリ(通常のテーブルと同じ)
SELECT * FROM view_name WHERE condition;
-- ビューを削除
DROP VIEW [IF EXISTS] view_name;
v_またはview_プレフィックスを付けることを推奨します。これにより実テーブルと容易に区別できます。
インデックスの構文
-- インデックスを作成
CREATE [UNIQUE|FULLTEXT] INDEX index_name ON table_name(column1, column2, ...);
-- インデックスを削除
DROP INDEX index_name ON table_name;
-- テーブルのインデックスを表示
SHOW INDEX FROM table_name;
EXPLAINを使用してクエリがインデックスを使用しているか確認できます:EXPLAIN SELECT * FROM employees WHERE last_name = 'Zhang';
例:ビューの作成と使用(難易度⭐)
-- 注文詳細ビューを作成
CREATE OR REPLACE VIEW v_order_detail AS
SELECT
o.order_id,
CONCAT(e.first_name, e.last_name) AS owner,
d.department_name AS department,
o.order_date,
o.total_amount AS amount,
o.status
FROM orders o
LEFT JOIN employees e ON o.employee_id = e.employee_id
LEFT JOIN departments d ON e.department_id = d.department_id;
-- ビューを使用してクエリ
SELECT * FROM v_order_detail WHERE status = 'completed';
出力:
order_id | owner | department | order_date | amount | status
---------+------------+------------+------------+----------+-----------
1001 | WangQiang | Sales | 2026-05-10 | 2500.00 | completed
1002 | WangQiang | Sales | 2026-05-15 | 1800.00 | completed
1005 | ZhaoMin | Sales | 2026-06-10 | 4100.00 | completed
-- ビューを使用して部門別の注文金額を集約
SELECT department, COUNT(*) AS order_count, SUM(amount) AS total_amount
FROM v_order_detail
GROUP BY department;
クエリのアプローチ:
- ビューは複雑な3テーブルJOINロジックをカプセル化
- 簡単なクエリだけで済む — JOINを再記述する必要なし
- 基盤データが変更されるとビューの結果は自動的に更新
例:インデックスを作成してクエリパフォーマンスを最適化(難易度⭐⭐)
-- 現在のクエリの実行計画を表示
EXPLAIN SELECT * FROM employees WHERE last_name = 'Zhang';
出力(インデックスなし):
+----+-------------+-----------+------+---------------+------+---------+------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------+------+---------------+------+---------+------+------+-------+
| 1 | SIMPLE | employees | ALL | NULL | NULL | NULL | NULL | 6 | Using where |
+----+-------------+-----------+------+---------------+------+---------+------+------+-------+
type = ALLはフルテーブルスキャンを示し、rows = 6はすべての行がスキャンされたことを示します。
-- インデックスを作成
CREATE INDEX idx_last_name ON employees(last_name);
-- 実行計画を再表示
EXPLAIN SELECT * FROM employees WHERE last_name = 'Zhang';
出力(インデックスあり):
+----+-------------+-----------+------+---------------+---------------+---------+-------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------+------+---------------+---------------+---------+-------+------+-------+
| 1 | SIMPLE | employees | ref | idx_last_name | idx_last_name | 152 | const | 1 | |
+----+-------------+-----------+------+---------------+---------------+---------+-------+------+-------+
type = refはインデックスが使用されていることを示し、rows = 1は1行のみスキャンされたことを示します。
-- 複合インデックスを作成
CREATE INDEX idx_status_date ON orders(status, order_date);
-- インデックスを使用できるクエリ
EXPLAIN SELECT * FROM orders WHERE status = 'completed' AND order_date > '2026-06-01';
-- テーブルのすべてのインデックスを表示
SHOW INDEX FROM employees;
クエリのアプローチ:
- EXPLAINの
typeカラム:ALL(フルテーブルスキャン)→ref(インデックス参照)はパフォーマンスの向上を示す rowsカラム:スキャン行数が少ないほど良い- 複合インデックスは最左プレフィックスルールに従って使用する必要がある
3. 一般的な応用シナリオ
シナリオ1:ビューで権限管理を簡素化
-- 機密情報を含まないビューを作成
CREATE OR REPLACE VIEW v_employee_public AS
SELECT
employee_id,
CONCAT(first_name, last_name) AS name,
d.department_name AS department
FROM employees e
LEFT JOIN departments d ON e.department_id = d.department_id;
-- 一般ユーザーはこのビューのみアクセスでき、給与やその他の機密情報にはアクセスできない
SELECT * FROM v_employee_public;
シナリオ2:ビューでデータレポートを実装
-- 月次売上レポートビューを作成
CREATE OR REPLACE VIEW v_monthly_sales AS
SELECT
DATE_FORMAT(order_date, '%Y-%m') AS month,
COUNT(*) AS order_count,
SUM(total_amount) AS total_sales,
AVG(total_amount) AS avg_order_amount,
COUNT(DISTINCT customer_id) AS customer_count
FROM orders
WHERE status != 'cancelled'
GROUP BY DATE_FORMAT(order_date, '%Y-%m');
-- レポートをクエリ
SELECT * FROM v_monthly_sales ORDER BY month;
出力:
month | order_count | total_sales | avg_order_amount | customer_count
--------+-------------+-------------+------------------+---------------
2026-05 | 2 | 4300.00 | 2150.00 | 2
2026-06 | 3 | 8250.00 | 2750.00 | 3
❓ よくある質問
質問:ビューと一時テーブルの違いは何ですか? 回答: ビューはデータを保存せず、クエリごとに基盤SQLを実行するため、結果は常に最新です;一時テーブルはデータスナップショットを保存し、クエリは高速ですがデータが古くなる可能性があります。ビューは長期的なクエリテンプレートに適し、一時テーブルは中間計算ステップに適しています。
質問:ビューはデータを変更できますか? 回答: ビューの定義によります。JOIN、集約関数、DISTINCT、サブクエリを含むビューは通常更新不可(SELECTのみ)です。シンプルな単一テーブルビュー(集約なし)はUPDATE可能です。実際には、ビューは主にクエリに使用され、ビューを通じたデータの変更は推奨しません。
質問:インデックスは多いほど良いですか? 回答: いいえ。インデックスはストレージスペースを消費し、INSERT/UPDATE/DELETEのパフォーマンスを低下させます。頻繁にクエリされ、高いカーディナリティ(多くのユニーク値)を持つカラムにのみインデックスを作成してください。小さなテーブルは通常インデックスが不要です。
質問:クエリがインデックスを使用しているかどうかはどうやって確認できますか? 回答:
EXPLAINコマンドを使用して実行計画を表示します。typeカラムに注目:ALL(フルテーブルスキャン、最も遅い)→index→range→ref→eq_ref→const(最も速い)。typeがALLでテーブルに大量のデータがある場合、インデックスが必要な可能性があります。
📖 まとめ
- ビューはSELECTクエリを保存する仮想テーブルで、データを保存せず、常に基盤テーブルと同期します
- ビューは複雑なクエリを簡素化し、データアクセスを制御し、クエリロジックを標準化できます
- JOIN/集約/DISTINCTを含むビューは通常更新不可
- インデックスはデータベースの「目次」であり、クエリを高速化しますが、ストレージと書き込みのオーバーヘッドが追加されます
- インデックスの種類:通常インデックス、ユニークインデックス、フルテキストインデックス、複合インデックス
- 最左プレフィックスルール:複合インデックスのクエリは最も左のカラムから一致する必要があります
EXPLAINを使用してクエリがインデックスを使用しているか分析します
📝 演習
演習1(⭐):給与が10000より高い従業員の情報(名前、給与、部門名)を表示するビューv_high_salaryを作成し、そのビューを使用して「技術」部門の高給与従業員をクエリしてください。
演習2(⭐⭐):部門名、従業員数、平均給与、最高給与、最低給与を含むビューv_department_reportを作成してください。その後、そのビューを使用して平均給与が最も高い部門を検索してください。
演習3(⭐⭐):ordersテーブルのcustomer_idカラムにインデックスを作成し、EXPLAINを使用してインデックス作成前後のクエリパフォーマンスを比較してください。その後、orders(status, order_date)に複合インデックスを作成し、どのクエリがそれを使用できるかテストしてください。
次のレッスン
👉 18-practice-aggregate - 練習:データ分析:集約関数、グループ化クエリ、条件式、高度な関数を一緒に適用し、実践シナリオを通じてデータ分析スキルを向上させましょう!



