この内容は古いバージョンです。最新バージョンを表示するには、戻るボタンを押してください。
バージョン:8
ページ更新者:atom
更新日時:2026-06-11 07:07:02

タイトル: SQL
SEOタイトル: 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 の知識が必須です。