SQLは、現代のデータ駆動型社会において、エンジニアだけでなくデータサイエンティストやマーケターにとっても必須のスキルとなっています。
2026年現在、AIによるクエリ生成ツールも普及していますが、その生成されたコードの妥当性を検証し、システムに合わせて最適化するためには、基礎となるSQLコマンドの深い理解が欠かせません。
本記事では、実務で頻繁に使用される基本的な構文から、高度なデータ分析に不可欠な応用クエリまで、目的別に整理して詳しく解説します。
これからSQLを学ぶ方から、リファレンスとして活用したい実務家の方まで、幅広く役立つ内容となっています。
SQLの基本分類と構造
SQL(Structured Query Language)は、関係データベース(RDBMS)を操作するための言語であり、その役割に応じて大きく4つのカテゴリに分類されます。
これらを理解することで、今自分が行おうとしている操作がデータベースの「構造」を変えるものなのか、それとも「データそのもの」を操作するものなのかを明確に区別できます。
| カテゴリ | 正称 | 主なコマンド | 役割 |
|---|---|---|---|
| DDL | Data Definition Language | CREATE, ALTER, DROP, TRUNCATE | テーブルやデータベースの構造を定義・変更する |
| DML | Data Manipulation Language | SELECT, INSERT, UPDATE, DELETE | データの抽出、追加、更新、削除を行う |
| DCL | Data Control Language | GRANT, REVOKE | ユーザーの権限を制御する |
| TCL | Transaction Control Language | COMMIT, ROLLBACK, SAVEPOINT | トランザクション(一連の処理)を制御する |
実務において最も触れる機会が多いのはDMLですが、データベースの設計やメンテナンスに関わる際にはDDLやTCLの知識が不可欠となります。
データ定義言語(DDL):構造を作成・管理する
データベースの枠組みを作るためのコマンド群です。
ここでは、テーブルの作成や変更について解説します。
CREATE:テーブルやデータベースの作成
新しいテーブルを作成する際には、列名(カラム名)とそのデータ型、制約を定義します。
-- 社員情報を管理するテーブルを作成
CREATE TABLE employees (
employee_id INT PRIMARY KEY, -- 社員ID(主キー)
first_name VARCHAR(50) NOT NULL, -- 名(必須)
last_name VARCHAR(50) NOT NULL, -- 姓(必須)
hire_date DATE DEFAULT CURRENT_DATE, -- 入社日(デフォルトは今日)
salary DECIMAL(10, 2) -- 給与(合計10桁、小数点2桁)
);
Query OK, 0 rows affected (0.05 sec)
ALTER:構造の変更
既存のテーブルに新しい列を追加したり、データ型を変更したりする場合に使用します。
-- 既存のテーブルに「メールアドレス」列を追加
ALTER TABLE employees ADD COLUMN email VARCHAR(100) UNIQUE;
DROPとTRUNCATE:削除の違い
DROPはテーブルそのものを削除し、TRUNCATEはテーブルの構造を残したまま全データを高速に消去します。
実務での使い分けには細心の注意が必要です。
データ操作言語(DML):データを扱う基本4操作
業務で最も多用される、いわゆる「CRUD」操作(Create, Read, Update, Delete)を実現するコマンドです。
SELECT:データの抽出
SQLの中で最も自由度が高く、奥が深いコマンドです。
特定の条件に合致するデータを取得します。
-- 2024年以降に入社した社員の氏名と給与を取得(給与の高い順)
SELECT
first_name,
last_name,
salary
FROM
employees
WHERE
hire_date >= '2024-01-01'
ORDER BY
salary DESC;
| first_name | last_name | salary |
|---|---|---|
| Taro | Tanaka | 850000.00 |
| Hanako | Sato | 720000.00 |
INSERT:データの追加
新しいレコードをテーブルに挿入します。
-- 1件のデータを追加
INSERT INTO employees (employee_id, first_name, last_name, salary)
VALUES (101, 'Ken', 'Suzuki', 600000);
-- 複数件をまとめて追加(バルクインサート)
INSERT INTO employees (employee_id, first_name, last_name, salary)
VALUES
(102, 'Yumi', 'Ito', 550000),
(103, 'Hiroki', 'Watanabe', 580000);
UPDATE:データの更新
既存のデータを書き換えます。
最も注意すべき点は、WHERE句を忘れないことです。
WHERE句を忘れると、テーブル内の全データが更新されてしまいます。
-- 特定の社員の給与を5%アップさせる
UPDATE employees
SET salary = salary * 1.05
WHERE employee_id = 101;
DELETE:データの削除
特定のレコードを削除します。
UPDATEと同様、WHERE句による範囲指定が極めて重要です。
-- 退職した特定の社員データを削除
DELETE FROM employees
WHERE employee_id = 103;
データの集計とグループ化
大量のデータから意味のある数値を導き出すために、集計関数とGROUP BYを組み合わせて使用します。
主な集計関数
COUNT():行数を数えるSUM():合計値を算出するAVG():平均値を算出するMAX()/MIN():最大値・最小値を取得する
GROUP BYとHAVING
特定の属性ごとに集計を行いたい場合はGROUP BYを使用します。
また、集計結果に対して条件を絞り込む場合は、WHEREではなくHAVINGを使います。
-- 部署ごとの平均給与を算出し、平均が50万以上の部署のみ表示
SELECT
department_id,
AVG(salary) AS avg_salary
FROM
employees
GROUP BY
department_id
HAVING
AVG(salary) >= 500000;
テーブルの結合(JOIN):複数データを紐付ける
リレーショナルデータベースの醍醐味は、複数のテーブルを関連付けて情報を取得することにあります。
INNER JOIN(内部結合)
両方のテーブルに一致するデータのみを取得します。
実務で最も使われる結合方法です。
-- 社員名とその所属部署名を取得
SELECT
e.last_name,
d.department_name
FROM
employees AS e
INNER JOIN
departments AS d
ON
e.department_id = d.department_id;
LEFT JOIN(左外部結合)
左側のテーブル(FROM句に記述したテーブル)を基準に、右側のテーブルに一致するデータがあれば結合し、なければNULLとして取得します。
-- 全社員を表示し、部署に所属していない場合は部署名をNULLにする
SELECT
e.last_name,
d.department_name
FROM
employees AS e
LEFT JOIN
departments AS d
ON
e.department_id = d.department_id;
応用的なクエリ手法
より複雑なロジックをSQLで記述するための高度なテクニックを紹介します。
サブクエリ(副問合せ)
SELECT文の結果を別のSELECT文で利用する方法です。
-- 全社員の平均給与よりも高い給与を得ている社員を抽出
SELECT
last_name,
salary
FROM
employees
WHERE
salary > (SELECT AVG(salary) FROM employees);
CTE(共通テーブル式)
WITH句を使用することで、複雑なクエリを整理し、可読性を高めることができます。
2026年現在の開発現場では、サブクエリよりもCTEの使用が推奨される傾向にあります。
-- 高額報酬者のリストを一時的なテーブルのように定義して利用
WITH high_salary_employees AS (
SELECT * FROM employees WHERE salary >= 800000
)
SELECT
last_name,
salary
FROM
high_salary_employees
WHERE
hire_date >= '2020-01-01';
ウィンドウ関数
集計を行いつつ、各行の個別のデータも保持したい場合に非常に強力なツールとなります。
-- 各部署内での給与ランキングを表示
SELECT
last_name,
department_id,
salary,
RANK() OVER (PARTITION BY department_id ORDER BY salary DESC) AS salary_rank
FROM
employees;
| last_name | department_id | salary | salary_rank |
|---|---|---|---|
| Tanaka | 10 | 900000 | 1 |
| Sato | 10 | 750000 | 2 |
| Suzuki | 20 | 800000 | 1 |
トランザクション制御(TCL)
データの整合性を保つために、複数の操作を一つの単位としてまとめます。
銀行振込のように「Aさんの口座を減らし、Bさんの口座を増やす」という一連の処理が、どちらか一方だけ成功しては困る場合に必須となります。
-- トランザクションの開始
BEGIN;
-- 処理1:在庫を減らす
UPDATE products SET stock = stock - 1 WHERE product_id = 501;
-- 処理2:注文履歴を追加
INSERT INTO orders (product_id, quantity) VALUES (501, 1);
-- すべて成功なら確定
COMMIT;
-- 途中でエラーが起きたら取り消し
-- ROLLBACK;
パフォーマンスと運用のためのコマンド
実務では、単に「動くクエリ」を書くだけでなく「速いクエリ」を書くことが求められます。
EXPLAIN:実行計画の確認
クエリがどのように実行されるかを表示します。
インデックスが効いているか、フルスキャン(全行探索)が発生していないかを確認するために不可欠です。
EXPLAIN SELECT * FROM employees WHERE last_name = 'Tanaka';
INDEX:検索の高速化
特定の列に対してインデックスを作成することで、データ検索速度を劇的に向上させます。
-- 苗字での検索を高速化するためにインデックスを作成
CREATE INDEX idx_last_name ON employees(last_name);
モダンSQL:JSONデータの取り扱い
2026年現在、多くのRDBMSがJSON形式のデータをネイティブにサポートしています。
NoSQLのような柔軟なデータ構造と、SQLの堅牢な分析能力を組み合わせることが可能です。
-- JSON型のカラムから特定のキーの値を抽出
SELECT
employee_id,
metadata->>'$.skill_set' AS skills
FROM
employees_extra
WHERE
metadata->>'$.location' = 'Tokyo';
このように、半構造化データを扱うコマンドも現代のSQLエンジニアには必須の知識となっています。
SQLを書く際のベストプラクティス
効率的でメンテナンス性の高いSQLを記述するためのポイントをまとめます。
- SELECT * を避ける:必要なカラムのみを指定することで、ネットワーク負荷とメモリ消費を抑えられます。
- エイリアス(別名)を活用する:テーブル名が長い場合、
AS eのように略称を付けることでクエリを読みやすくします。 - インデックスを意識したWHERE句:関数を列に適用(例:
WHERE YEAR(date) = 2026)するとインデックスが使われない場合があるため、注意が必要です。 - 大文字と小文字の統一:SQLキーワードを大文字、テーブル名やカラム名を小文字に統一することで、可読性が向上します。
まとめ
SQLコマンドは、その一つひとつがシンプルな役割を持っていますが、これらを組み合わせることで、テラバイト級の膨大なデータから価値のある情報を瞬時に引き出すことが可能になります。
本記事で紹介したDDL(定義)、DML(操作)、結合、そしてウィンドウ関数などの応用技術は、どのようなデータベース製品(PostgreSQL, MySQL, SQL Server, BigQuery等)を使用する場合でも共通する「一生モノのスキル」です。
特に2020年代後半のエンジニアリングにおいては、AIに指示を出す「プロンプト」の裏側でもSQLの論理構造が働いています。
コマンドの丸暗記に留まらず、「データがどのように結びつき、どのように集計されるのか」という論理的なプロセスを意識して、日々の業務に活用してください。
基本を確実に押さえ、必要に応じて高度な構文をリファレンスとして参照することで、より高度なデータ活用が実現できるはずです。
