4.

SQL 入門 — リレーショナル DB の問い合わせ言語・JOIN・GROUP BY・主要 RDBMS の方言

編集
この記事の要点
  • SQL(Structured Query Language)はリレーショナルデータベースを操作する標準言語。ISO/IEC 9075 として標準化
  • カテゴリ: DDL(CREATE/ALTER/DROP)/ DML(SELECT/INSERT/UPDATE/DELETE)/ DCL(GRANT/REVOKE)/ TCL(COMMIT/ROLLBACK)
  • JOIN: INNER(共通分のみ)/ LEFT(左側全部)/ RIGHT(右側全部)/ FULL(両側全部)/ CROSS(直積)
  • 集約関数: COUNT / SUM / AVG / MAX / MIN + GROUP BY + HAVING。サブクエリで複雑な条件を組める
  • 主要 RDBMS: MySQL / PostgreSQL / SQL Server / Oracle / SQLite。ANSI SQL は共通だが方言あり

SQL とは

SQL(Structured Query Language、シーケル / エスキューエル)は、リレーショナルデータベース管理システム (RDBMS) でデータを問い合わせ・操作するための宣言型言語です。1970 年代に IBM の Edgar F. Codd の関係モデル理論を基に設計されました。

ISO/IEC 9075 として国際標準化されており、SQL:1992 / SQL:1999 / SQL:2003 / ... / SQL:2023 と継続的に更新されています。RDBMS ごとに方言(SQL 文法の差)があります。

SQL のカテゴリ

カテゴリ意味主なコマンド
DDLデータ定義言語CREATE、ALTER、DROP、TRUNCATE
DMLデータ操作言語SELECT、INSERT、UPDATE、DELETE
DCLデータ制御言語GRANT、REVOKE
TCLトランザクション制御BEGIN、COMMIT、ROLLBACK、SAVEPOINT

テーブル作成 (DDL)

-- テーブル作成
CREATE TABLE users (
    id          INT PRIMARY KEY AUTO_INCREMENT,
    name        VARCHAR(100) NOT NULL,
    email       VARCHAR(255) UNIQUE NOT NULL,
    age         INT CHECK (age >= 0),
    created_at  DATETIME DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE orders (
    id          INT PRIMARY KEY AUTO_INCREMENT,
    user_id     INT NOT NULL,
    product     VARCHAR(255),
    price       DECIMAL(10, 2),
    ordered_at  DATETIME DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);

-- カラム追加
ALTER TABLE users ADD COLUMN phone VARCHAR(20);

-- インデックス作成
CREATE INDEX idx_users_email ON users(email);

-- テーブル削除
DROP TABLE orders;

基本の CRUD (DML)

-- INSERT (Create)
INSERT INTO users (name, email, age) VALUES
    ('Alice', 'alice@example.com', 30),
    ('Bob',   'bob@example.com',   25),
    ('Carol', 'carol@example.com', 35);

-- SELECT (Read)
SELECT * FROM users;
SELECT name, email FROM users WHERE age >= 30;
SELECT * FROM users ORDER BY age DESC LIMIT 10 OFFSET 20;

-- UPDATE
UPDATE users SET age = 31 WHERE name = 'Alice';
UPDATE users SET age = age + 1 WHERE age < 30;

-- DELETE
DELETE FROM users WHERE id = 5;
DELETE FROM users WHERE created_at < '2025-01-01';

-- ヒント: UPDATE / DELETE は必ず WHERE を確認
-- 本番では BEGIN; ... COMMIT; or ROLLBACK; を使う

JOIN(複数テーブルの結合)

-- INNER JOIN: 両側にマッチがある行のみ
SELECT u.name, o.product, o.price
FROM users u
INNER JOIN orders o ON o.user_id = u.id;

-- LEFT JOIN: 左側 (users) 全行 + 右側にマッチがあれば
SELECT u.name, COUNT(o.id) AS order_count
FROM users u
LEFT JOIN orders o ON o.user_id = u.id
GROUP BY u.id, u.name;

-- RIGHT JOIN: 右側全行(LEFT JOIN を入れ替えても良い)
SELECT u.name, o.product
FROM users u
RIGHT JOIN orders o ON o.user_id = u.id;

-- FULL OUTER JOIN: 両側の全行(PostgreSQL/SQL Server、MySQL 8.0 は UNION で代替)
SELECT u.name, o.product
FROM users u
FULL OUTER JOIN orders o ON o.user_id = u.id;

-- CROSS JOIN: 直積(全組み合わせ)
SELECT u.name, c.name AS category
FROM users u
CROSS JOIN categories c;

-- 自己結合 (Self Join)
SELECT e1.name AS employee, e2.name AS manager
FROM employees e1
LEFT JOIN employees e2 ON e1.manager_id = e2.id;

集約関数と GROUP BY

-- 行数
SELECT COUNT(*) FROM users;
SELECT COUNT(DISTINCT email) FROM users;

-- 合計・平均・最大・最小
SELECT
    COUNT(*)     AS total,
    SUM(price)   AS total_sales,
    AVG(price)   AS avg_price,
    MAX(price)   AS max_price,
    MIN(price)   AS min_price
FROM orders;

-- GROUP BY: ユーザーごとの注文集計
SELECT
    user_id,
    COUNT(*)   AS order_count,
    SUM(price) AS total_spent,
    AVG(price) AS avg_spent
FROM orders
GROUP BY user_id;

-- HAVING: 集約後のフィルタ
SELECT user_id, COUNT(*) AS cnt
FROM orders
GROUP BY user_id
HAVING COUNT(*) >= 3;

-- ORDER BY と LIMIT
SELECT user_id, SUM(price) AS total
FROM orders
GROUP BY user_id
ORDER BY total DESC
LIMIT 10;

サブクエリ

-- 平均年齢より上のユーザー
SELECT name, age
FROM users
WHERE age > (SELECT AVG(age) FROM users);

-- 注文があるユーザーだけ
SELECT * FROM users
WHERE id IN (SELECT DISTINCT user_id FROM orders);

-- 存在判定(EXISTS は高速)
SELECT * FROM users u
WHERE EXISTS (
    SELECT 1 FROM orders o WHERE o.user_id = u.id
);

-- 相関サブクエリ
SELECT
    u.name,
    (SELECT COUNT(*) FROM orders o WHERE o.user_id = u.id) AS order_count
FROM users u;

-- 共通テーブル式 (CTE)
WITH high_spenders AS (
    SELECT user_id, SUM(price) AS total
    FROM orders
    GROUP BY user_id
    HAVING SUM(price) > 10000
)
SELECT u.name, h.total
FROM high_spenders h
JOIN users u ON u.id = h.user_id;

主要 RDBMS と方言

RDBMS運営特徴・方言
MySQLOracle (元 Sun)Web で最も普及、AUTO_INCREMENT、LIMIT/OFFSET、バックティック
MariaDBコミュニティMySQL フォーク、互換性高い
PostgreSQLコミュニティ標準準拠が高い、JSONB、配列型、高度な機能
SQL ServerMicrosoftTOP n、IDENTITY、T-SQL、ストアド・ファンクション強力
Oracle DatabaseOracleエンタープライズ、PL/SQL、シーケンス、ROWNUM
SQLiteD. Richard Hippファイルベース、組込用途、モバイル / Electron

インデックスの基本

大量データの検索高速化に必須。よく WHERE / JOIN / ORDER BY されるカラムに付ける:

-- 単一カラム
CREATE INDEX idx_users_email ON users(email);

-- 複合インデックス(左から順に効く)
CREATE INDEX idx_orders_user_date ON orders(user_id, ordered_at);

-- UNIQUE インデックス(重複禁止)
CREATE UNIQUE INDEX idx_users_email_unique ON users(email);

-- 実行計画確認
EXPLAIN SELECT * FROM users WHERE email = 'alice@example.com';
EXPLAIN ANALYZE SELECT ...;   -- PostgreSQL は実測値も

FAQ

Q: NoSQL と RDBMS どちらを選ぶ?
A: 関係性とトランザクションが重要(決済、業務システム)なら RDBMS。スケールアウトと柔軟スキーマ(ログ、IoT、SNS のフィード)なら NoSQL(MongoDB、DynamoDB 等)。

Q: SELECT * は使ってよい?
A: 開発時の確認なら OK だが、本番コードでは避ける。カラム追加で予期せぬ挙動になる、ネットワーク・I/O 負荷も上がる。

Q: トランザクションとは?
A: BEGINCOMMIT の間の処理を原子的(全部成功 or 全部取り消し)に扱うこと。ACID 特性(原子性 / 一貫性 / 独立性 / 永続性)の保証。

Q: ORM(Eloquent、ActiveRecord)があれば SQL は不要?
A: 簡単なクエリは ORM で十分だが、パフォーマンスチューニング / 複雑な集計 / 移行スクリプトでは生 SQL の知識が必須です。

編集
Post Share
子ページ
  1. 基本構文
  2. データベース関連
  3. テーブル関連
  4. ユーザー関連
  5. メタデータ関連
  6. NULL判定を伴うCASE分の使用方法
同階層のページ
  1. ダウンロード&インストール方法(Windows)
  2. インストール方法(Linux)
  3. コマンド一覧
  4. SQL
  5. データ型
  6. 関数
  7. 管理ツール
  8. 設定
  9. パフォーマンスチューニング関連
  10. エクスポートおよびインポート
  11. エラー&トラブル
  12. 文字コードの確認
  13. 実行中の SQL の状態確認およびプロセスキルの方法
  14. パスワードの無効化設定
  15. root ユーザーの初期パスワード確認方法
  16. rootユーザーのパスワード変更方法
  17. LIMIT, OFFSET の始まりと挙動
  18. mysqlのバージョン確認方法
  19. 実行計画の表示方法
  20. レプリケーションのステータス確認方法
  21. 中央値の導き方(バージョン8未満)
  22. 階層SQL(バージョン8未満)
  23. パーセンタイルの導き方
  24. 特定スキーマの全テーブルの全カラム情報を取得する方法
  25. MySQLで文字列の置換をする方法