第6章 SQL応用

更新日:2025年12月28日

🎧

この章を音声で聴く

再生速度:
本章では、SQLの応用的な機能について解説する。集約関数(COUNT、SUM、AVG等)とGROUP BY句によるグループ化、HAVING句によるグループの絞り込み、サブクエリの活用、ビューの作成と利用、インデックスの仕組みと作成方法を学ぶ。これらの機能を習得することで、より複雑なデータ分析や効率的なクエリが可能になる。

1. 集約関数

1.1 COUNT・SUM・AVG

集約関数(Aggregate Function)は、複数の行から単一の値を計算する関数である。

Table 1. 主要な集約関数

関数 説明 NULL の扱い
COUNT(*) 行数をカウント NULLを含む全行
COUNT(列名) 非NULL値の数 NULLを除外
SUM(列名) 合計値 NULLを除外
AVG(列名) 平均値 NULLを除外
MAX(列名) 最大値 NULLを除外
MIN(列名) 最小値 NULLを除外
-- 基本的な集約関数
SELECT 
    COUNT(*) AS total_employees,
    COUNT(department_id) AS employees_with_dept,
    SUM(salary) AS total_salary,
    AVG(salary) AS average_salary,
    MAX(salary) AS max_salary,
    MIN(salary) AS min_salary
FROM employees;

-- DISTINCT と組み合わせ
SELECT COUNT(DISTINCT department_id) AS dept_count FROM employees;

-- 条件付きカウント(CASE式)
SELECT 
    COUNT(*) AS total,
    COUNT(CASE WHEN salary >= 500000 THEN 1 END) AS high_salary_count,
    COUNT(CASE WHEN salary < 500000 THEN 1 END) AS low_salary_count
FROM employees;

1.2 GROUP BY・HAVING

GROUP BY句は、指定した列の値ごとにグループ化し、各グループに対して集約関数を適用する。HAVING句は、グループ化後の条件指定に使用する。

-- 部署ごとの社員数と平均給与
SELECT 
    department_id,
    COUNT(*) AS employee_count,
    AVG(salary) AS avg_salary
FROM employees
GROUP BY department_id;

-- HAVING句でグループを絞り込み
-- 社員数が5人以上の部署のみ表示
SELECT 
    department_id,
    COUNT(*) AS employee_count,
    AVG(salary) AS avg_salary
FROM employees
GROUP BY department_id
HAVING COUNT(*) >= 5;

-- WHERE と HAVING の違い
-- WHERE: GROUP BY 前の行の絞り込み
-- HAVING: GROUP BY 後のグループの絞り込み
SELECT 
    department_id,
    AVG(salary) AS avg_salary
FROM employees
WHERE salary > 300000           -- 個々の行に対する条件
GROUP BY department_id
HAVING AVG(salary) > 450000;    -- グループに対する条件

Table 2. WHERE と HAVING の比較

項目 WHERE HAVING
実行タイミング GROUP BY の前 GROUP BY の後
対象 個々の行 グループ
集約関数の使用 不可 可能
ORDER BY と GROUP BY
GROUP BY と ORDER BY を組み合わせることで、グループ化した結果を並び替えできる。ORDER BY は最後に実行される。
-- 部署ごとの平均給与を高い順に表示
SELECT 
    department_id,
    AVG(salary) AS avg_salary
FROM employees
GROUP BY department_id
ORDER BY avg_salary DESC;

2. サブクエリ

サブクエリ(副問い合わせ)は、SQL文の中に埋め込まれた別のSELECT文である。外側のクエリから参照される内側のクエリとして機能する。

Table 3. サブクエリの種類

種類 配置場所 戻り値
スカラサブクエリ SELECT句、WHERE句 単一値
行サブクエリ WHERE句 単一行
テーブルサブクエリ FROM句 複数行(テーブル)
相関サブクエリ WHERE句 外側クエリの各行に依存
-- スカラサブクエリ(単一値を返す)
-- 平均給与より高い給与の社員を取得
SELECT name, salary
FROM employees
WHERE salary > (SELECT AVG(salary) FROM employees);

-- IN を使ったサブクエリ(複数値)
-- 営業部門の社員を取得
SELECT name, salary
FROM employees
WHERE department_id IN (
    SELECT id FROM departments WHERE name LIKE '%営業%'
);

-- FROM句でのサブクエリ(派生テーブル)
SELECT dept_avg.department_id, dept_avg.avg_salary
FROM (
    SELECT department_id, AVG(salary) AS avg_salary
    FROM employees
    GROUP BY department_id
) AS dept_avg
WHERE dept_avg.avg_salary > 450000;

-- 相関サブクエリ
-- 各部署で最高給与の社員を取得
SELECT e1.name, e1.salary, e1.department_id
FROM employees e1
WHERE e1.salary = (
    SELECT MAX(e2.salary)
    FROM employees e2
    WHERE e2.department_id = e1.department_id
);

-- EXISTS / NOT EXISTS
-- 注文がある顧客のみ表示
SELECT c.name
FROM customers c
WHERE EXISTS (
    SELECT 1 FROM orders o WHERE o.customer_id = c.id
);

3. ビュー

ビュー(View)は、SELECT文の結果を仮想的なテーブルとして保存したものである。データは実際には格納されず、参照時にSELECT文が実行される。

Table 4. ビューの利点

利点 説明
複雑なクエリの簡略化 頻繁に使うJOINやサブクエリをビューにまとめる
セキュリティ 特定の列のみを公開し、機密データを隠す
論理的独立性 テーブル構造が変わってもビューで吸収
データの整合性 WITH CHECK OPTION で不正な更新を防止
-- ビューの作成
CREATE VIEW employee_summary AS
SELECT 
    e.id,
    e.name,
    e.salary,
    d.name AS department_name
FROM employees e
LEFT JOIN departments d ON e.department_id = d.id;

-- ビューの使用(テーブルと同様に参照)
SELECT * FROM employee_summary WHERE salary > 500000;

-- ビューの置き換え(すでに存在する場合は上書き)
CREATE OR REPLACE VIEW employee_summary AS
SELECT 
    e.id,
    e.name,
    e.salary,
    d.name AS department_name,
    e.hire_date
FROM employees e
LEFT JOIN departments d ON e.department_id = d.id;

-- ビューの削除
DROP VIEW employee_summary;

-- 更新可能ビュー(単一テーブル、集約なし)
CREATE VIEW active_employees AS
SELECT id, name, salary, department_id
FROM employees
WHERE status = 'active'
WITH CHECK OPTION;  -- status='active'以外のINSERT/UPDATEを禁止

-- ビューへのINSERT(更新可能な場合)
INSERT INTO active_employees (id, name, salary, department_id)
VALUES (100, '新入社員', 350000, 1);
マテリアライズドビュー
一部のDBMS(PostgreSQL、Oracle等)は、クエリ結果を実際にディスクに保存する「マテリアライズドビュー」をサポートする。大量データの集計結果をキャッシュし、パフォーマンスを向上させる用途に使用される。

4. インデックス

インデックス(Index)は、テーブル内のデータを高速に検索するためのデータ構造である。書籍の索引と同様の役割を果たす。

4.1 B-Tree

B-Tree(Balanced Tree)は、最も一般的なインデックス構造である。データを木構造で管理し、ルートから目的のデータまで効率的に探索できる。

Table 5. インデックスの種類

種類 特徴 用途
B-Tree バランス木、範囲検索に強い 一般的な検索(デフォルト)
Hash ハッシュテーブル、等価検索のみ 完全一致検索
GiST 汎用検索木 空間データ、全文検索
GIN 転置インデックス 配列、JSON、全文検索

Table 6. インデックスの効果

操作 インデックスなし インデックスあり
検索(WHERE) 全件スキャン O(n) 木探索 O(log n)
INSERT 高速 インデックス更新で遅くなる
UPDATE 高速 インデックス更新で遅くなる
DELETE 高速 インデックス更新で遅くなる

4.2 作成と削除

-- 単一列インデックスの作成
CREATE INDEX idx_employees_name ON employees(name);

-- 複合インデックス(複数列)
CREATE INDEX idx_employees_dept_salary 
ON employees(department_id, salary);

-- ユニークインデックス(重複不可)
CREATE UNIQUE INDEX idx_employees_email ON employees(email);

-- インデックスの削除
DROP INDEX idx_employees_name;

-- インデックスの確認(PostgreSQL)
\di employees*

-- インデックスの確認(MySQL)
SHOW INDEX FROM employees;

Table 7. インデックス作成の指針

推奨 非推奨
WHERE句で頻繁に使う列 更新が非常に多い列
JOIN条件の列 カーディナリティが低い列(性別等)
ORDER BY で使う列 小さなテーブル(数百行以下)
外部キー ほとんど参照されない列
インデックスの過剰作成に注意
インデックスは検索を高速化するが、INSERT/UPDATE/DELETE時にインデックスの更新が必要となる。インデックスが多すぎると書き込み性能が低下し、ストレージも消費する。必要なインデックスのみを作成すること。

5. まとめ

Table 8. 第6章のまとめ

概念 内容
集約関数 COUNT, SUM, AVG, MAX, MIN - NULLの扱いに注意
GROUP BY 列の値でグループ化、集約関数と組み合わせ
HAVING グループ化後の条件指定(WHEREとの違い)
サブクエリ スカラ、行、テーブル、相関サブクエリ
EXISTS 行の存在確認、相関サブクエリと組み合わせ
ビュー 仮想テーブル、クエリの簡略化、セキュリティ
インデックス 検索の高速化、B-Tree、トレードオフ
参考・免責事項
本コンテンツは2025年12月時点の情報に基づいて作成されています。SQLの高度な機能はDBMS製品により実装が異なる場合があります。詳細は各製品のドキュメントをご確認ください。