SQLは「データベースへお願いを伝える言葉」です
SQL(Structured Query Language)は、データベースに対して「見せて」「追加して」「直して」「削除して」と依頼するための言語です。サーバー構築・運用では、アプリの接続確認、監視データの調査、障害時の切り分け、バックアップ後の確認でも使います。
SELECT → 絞る WHERE → 並べる ORDER BY → 集計する GROUP BY → つなぐ JOIN → 変更する INSERT / UPDATE / DELETE の順で練習します。DQL(Data Query Language/データを問い合わせる命令):読む
SELECT で必要な行と列を取得します。調査は原則ここから始めます。
DML(Data Manipulation Language/データを操作する命令):データを変える
INSERT / UPDATE / DELETE で行を追加・更新・削除します。
DDL(Data Definition Language/データの入れ物を定義する命令):器を作る
CREATE / ALTER / DROP でテーブルなどの構造を管理します。ここでいう「器」とは、データを入れる表そのもののことです。
DCL(Data Control Language/権限を制御する命令):権限を守る
GRANT / REVOKE で「誰が何をできるか」を制御します。
TCL(Transaction Control Language/変更のまとまりを制御する命令):変更をまとめる
COMMIT / ROLLBACK で変更の確定・取り消しを行います。
運用:速さと安全を確かめる
EXPLAIN、インデックス、ログ、バックアップを組み合わせて確認します。
| 分類 | MySQL | PostgreSQL | SQLite | SQL Server |
|---|---|---|---|---|
| 現在日時を取る日付関数 | NOW() | NOW() / CURRENT_TIMESTAMP | CURRENT_TIMESTAMP | GETDATE() |
| 自動採番 | AUTO_INCREMENT | GENERATED ... AS IDENTITY / SERIAL | INTEGER PRIMARY KEY(自動的に採番) | IDENTITY(1,1) |
| 文字列結合 | CONCAT(a, b) | a || b | a || b | a + b |
| 件数制限 | LIMIT n | LIMIT n | LIMIT n | TOP n |
練習用テーブル:servers
以降は、サーバー台帳を想定した次の列を使います。1行が1台のサーバー、列がその属性です。owner_id は担当者を表す番号で、結合(JOIN)で使う owners テーブルの主キー(その表の中で1行を見分けるための、重複しない列)に対応します。
| 列名 | 意味 | 例 |
|---|---|---|
server_id | 重複しない識別番号 | 101 |
hostname | サーバー名 | web-01 |
role | 役割 | web / db |
environment | 環境 | dev / prod |
owner_id | 担当者ID(ownersテーブルへの外部キー、未設定はNULL) | 1 / NULL |
cpu_usage | CPU使用率(%) | 42.5 |
last_checked_at | 最終確認日時 | 2026-08-28 10:00:00 |
この列に、以下のような架空のサンプルデータが入っているものとして、このページの例と演習を進めます。
| server_id | hostname | role | environment | owner_id | cpu_usage | last_checked_at |
|---|---|---|---|---|---|---|
| 101 | web-01 | web | prod | 1 | 82.3 | 2026-08-28 09:50:00 |
| 102 | web-02 | web | prod | 2 | 45.1 | 2026-08-28 09:55:00 |
| 103 | db-01 | db | prod | 1 | 91.7 | 2026-08-28 10:00:00 |
| 104 | db-02 | db | dev | NULL | 15.2 | 2026-08-20 08:00:00 |
| 105 | app-01 | app | stg | 2 | 66.4 | 2026-08-27 22:10:00 |
| 106 | app-02 | app | prod | NULL | 88.9 | 2026-08-28 09:40:00 |
| 107 | batch-01 | batch | dev | 3 | 12.0 | 2026-08-15 07:30:00 |
| 108 | cache-01 | cache | prod | 3 | 77.5 | 2026-08-28 09:45:00 |
1. データを読む・絞る・並べる
SELECT
表示したい列を指定します。* と書くと、すべての列を取り出せます。ただし実務では、必要な列だけを書き並べます。何を見たいのかが他の人に伝わり、無駄なデータのやり取りも減らせるからです。
FROM
どのテーブルから読むかを指定します。SELECT hostname FROM servers のように使います。
WHERE
条件に合う行だけに絞り込みます。文字のデータは、通常シングルクォート(')で囲みます。
ORDER BY
ASC は昇順、DESC は降順です。省略時は通常 ASC です。
DISTINCT
重複する結果を1つにまとめます。環境名の一覧取得などに使います。
LIMIT
返す行数を制限します。MySQL・PostgreSQL・SQLiteで利用できます。SQL Serverは TOP などを使います。
SELECT hostname, role, cpu_usage
FROM servers
WHERE environment = 'prod' AND cpu_usage >= 80
ORDER BY cpu_usage DESC
LIMIT 10;このデータに対する実行結果(#sampleのサンプルデータで再現した例。架空のデータであり、実機で実行した記録ではありません):
| hostname | role | cpu_usage |
|---|---|---|
| db-01 | db | 91.7 |
| app-02 | app | 88.9 |
| web-01 | web | 82.3 |
読み解き方:上のSQLを「本番環境(prod)でCPU使用率が80%以上のサーバーを、使用率が高い順に最大10台表示する」と日本語へ戻せれば理解できています。面接では「条件で絞り、並べ替え、件数を制限する流れを説明できる」と言えます。
=、<>(等しくない)、AND、OR、NOT、IN (...)(かっこ内のどれかに一致)、BETWEEN A AND B(AからBまでの範囲に入る)、LIKE 'web-%'(%は任意の文字列を表すので「web-」で始まる名前に一致)、IS NULL(値が入っていない)。NULL は値がない状態なので = NULL ではなく IS NULL を使います。2. 数える・まとめる
かんたんに言うとここでは、1行ずつ見るのをやめて「同じ仲間ごとにまとめて数える」方法を学びます。まとめる単位を決めるのが GROUP BY、まとめたあとの結果を絞り込むのが HAVING です。
COUNT / SUM
COUNT(*) は行数、SUM(column) は合計を求めます。
AVG / MIN / MAX
平均・最小・最大を求めます。監視値の傾向確認に便利です。
GROUP BY
同じ値ごとに行をまとめます。環境別・役割別の台数集計に使います。
HAVING
WHERE は、まとめる前の行を1行ずつ絞り込みます。HAVING は、GROUP BY でまとめたあとのグループを絞り込みます。
AS
列やテーブルへ分かりやすい別名を付けます。AS server_count のように使います。
CASE
値に応じて表示を変えます。CPU使用率を「正常・警告・異常」に分類できます。CASE WHEN cpu_usage >= 90 THEN '異常' WHEN cpu_usage >= 70 THEN '警告' ELSE '正常' END のように使います。
SELECT environment, COUNT(*) AS server_count, AVG(cpu_usage) AS avg_cpu
FROM servers
GROUP BY environment
HAVING AVG(cpu_usage) >= 60
ORDER BY avg_cpu DESC;このデータに対する実行結果(#sampleのサンプルデータで再現した架空の例):
| environment | server_count | avg_cpu |
|---|---|---|
| prod | 5 | 77.10 |
| stg | 1 | 66.40 |
devは平均CPU使用率が13.6と60未満のため、HAVINGで除外されて結果に含まれません。
3. 複数のテーブルをつなぐ
servers.owner_id と owners.owner_id のように、両方の表に共通する列をキー(つなぎ合わせるための目印)として使います。こうして、別々の表に分けて保存したデータを1つの結果にまとめます。担当者を管理する owners テーブルは、次の列を持つものとします。
| owner_id | owner_name |
|---|---|
| 1 | インフラ担当 |
| 2 | アプリ担当 |
| 3 | 監視担当 |
INNER JOIN
両方のテーブルに一致する行だけを返します。
LEFT JOIN
左側をすべて返し、右側に一致がなければ右側の列は NULL になります。担当未設定の発見に便利です。
ON
どの列同士を一致させるかを書きます。この条件が足りないと、結果の行数が想定外に増えてしまいます。
SELECT s.hostname, s.environment, o.owner_name
FROM servers AS s
LEFT JOIN owners AS o ON s.owner_id = o.owner_id
WHERE s.environment = 'prod';このデータに対する実行結果(#sample・上記ownersのサンプルデータで再現した架空の例):
| hostname | environment | owner_name |
|---|---|---|
| web-01 | prod | インフラ担当 |
| web-02 | prod | アプリ担当 |
| db-01 | prod | インフラ担当 |
| app-02 | prod | NULL |
| cache-01 | prod | 監視担当 |
app-02はowner_idがNULLのため、LEFT JOINにより行は残りつつowner_nameがNULLになります。これが演習3で探す「担当者未設定」の状態です。
ON の条件が足りていないかを疑います。4. データを追加・更新・削除する
INSERT INTO
新しい行を追加します。列名と値の対応を明示します。
UPDATE / SET
既存行の値を変更します。WHERE がないと全行更新になるため要注意です。
DELETE FROM
行を削除します。WHERE がないと全行削除になります。
INSERT INTO servers (server_id, hostname, role, environment, cpu_usage)
VALUES (104, 'web-02', 'web', 'dev', 15.2);
UPDATE servers
SET last_checked_at = CURRENT_TIMESTAMP
WHERE server_id = 104;
DELETE FROM servers
WHERE server_id = 104 AND environment = 'dev';WHERE を付けた SELECT を実行します。どの行が何件変わるのかを、先に自分の目で確認するためです。可能ならトランザクション(複数の変更をひとまとめにして、失敗したら取り消せる仕組み)の中で実行します。本番環境では、これに加えて作業の承認、バックアップの取得、切り戻し手順(元の状態へ戻す手順)の用意、作業ログの記録もそろえる必要があります。5. テーブルと制約を定義する
かんたんに言うとデータを入れる前に、表の形と「入れてよい値の決まり」を先に決めます。この決まりを制約と呼びます。制約を付けておけば、間違ったデータがそもそも入らなくなります。
CREATE TABLE
テーブル名、列名、データ型(その列に入る値の種類)、制約(値が満たすべき決まり)を決めて、新しい表を作ります。
ALTER TABLE
列や制約を追加・変更します。製品によって対応範囲が異なります。ALTER TABLE servers ADD COLUMN memo VARCHAR(200); のように使います。
DROP TABLE
テーブル構造とデータを削除します。復旧が難しい破壊的操作です。DROP TABLE servers; のように使います。
PRIMARY KEY
各行を一意に識別します。重複と NULL を許しません。
FOREIGN KEY
別テーブルのキーを参照し、存在しない担当者IDなどを防ぎます。
NOT NULL / UNIQUE / CHECK / DEFAULT
NOT NULLは必須入力、UNIQUEは重複禁止、CHECKは値が満たすべき条件、DEFAULTは初期値を表します。アプリ側だけでなく、データベース側でもこれらの決まりを守らせます。
CREATE TABLE owners (
owner_id INTEGER PRIMARY KEY,
owner_name VARCHAR(50) NOT NULL
);
CREATE TABLE servers (
server_id INTEGER PRIMARY KEY,
hostname VARCHAR(100) NOT NULL UNIQUE,
role VARCHAR(30) NOT NULL,
environment VARCHAR(10) NOT NULL CHECK (environment IN ('dev', 'stg', 'prod')),
owner_id INTEGER,
cpu_usage DECIMAL(5,2) DEFAULT 0,
last_checked_at TIMESTAMP,
FOREIGN KEY (owner_id) REFERENCES owners (owner_id)
);| 型 | 意味 |
|---|---|
INTEGER | 整数 |
VARCHAR(n) | 最大n文字までの可変長文字列(例:VARCHAR(100)は最大100文字) |
DECIMAL(p,s) | 合計p桁のうち小数点以下s桁を持つ数値(例:DECIMAL(5,2)は999.99まで表現可能) |
TIMESTAMP | 日付と時刻 |
6. トランザクションで変更をひとまとまりにする
複数の変更を「すべて成功」または「すべて取り消し」にする仕組みです。途中だけ反映される不整合を防ぎます。
BEGIN;
UPDATE servers SET environment = 'prod' WHERE server_id = 104;
SELECT server_id, hostname, environment FROM servers WHERE server_id = 104;
-- 問題なければ確定
COMMIT;
-- 問題があれば COMMIT の代わりに ROLLBACK;CREATE TABLE などのDDLを実行すると、その時点で変更が自動的に確定されてしまう製品もあります。使う製品ごとに確認します。7. 権限と安全なSQL
まず、SQLインジェクション(利用者が入力した文字を悪用して、想定外のSQLを実行させる攻撃)という典型的な事故の悪い例を見ます。利用者が入力した文字を、そのままSQL文へつなぎ合わせてはいけません。入力の内容しだいで、作った人が想定していないSQLが実行されてしまいます。
-- 悪い例:入力値をそのまま文字列連結している(アプリ側の疑似コード、実行しないでください)
sql = "SELECT * FROM servers WHERE hostname = '" + input + "'";
-- 利用者が hostname 欄に ' OR '1'='1 と入力すると、
-- 実際に実行される文は次のようになってしまう
SELECT * FROM servers WHERE hostname = '' OR '1'='1';
-- WHERE条件が常に真になり、絞り込みたかった一部の行ではなく全行が返るGRANT
読み取りなど必要な権限だけを付与します。最小権限の原則を守ります。
REVOKE
不要になった権限を取り消します。退職・異動・役割変更時も見直します。
プレースホルダー
利用者が入力した値を、SQLの文字列へ直接つなぎません。値を入れる場所だけを空けておいたSQLを先に用意する、プリペアドステートメント(準備済みのSQL)という仕組みを使います。空けておいた場所がプレースホルダーです。これでSQLインジェクションを防げます。
GRANT SELECT ON servers TO monitoring_user;
REVOKE UPDATE, DELETE ON servers FROM monitoring_user;
-- アプリ側の考え方(記法はライブラリで異なる)
SELECT hostname FROM servers WHERE server_id = ?;8. インデックス・ビュー・実行計画
かんたんに言うと「なぜ遅いのか」を調べて速くする章です。インデックスは本の索引にあたる検索の近道、ビューはよく使うSQLに付けた名前、実行計画はデータベースがどう処理するつもりかを見せてくれる説明書です。
CREATE INDEX
本の索引のように検索を速くします。ただし容量を使い、追加・更新・削除が遅くなることがあります。
CREATE VIEW
よく使う問い合わせに名前を付けます。複雑さを隠せますが、必ず高速になるわけではありません。CREATE VIEW prod_servers AS SELECT hostname, cpu_usage FROM servers WHERE environment = 'prod'; のように使います。
EXPLAIN
データベースがそのSQLをどう処理するつもりかを表示します。全件走査(表を先頭から最後まで全部読む方法)になっていないか、どのインデックスを使うか、どの順でテーブルをつなぐかを確認できます。なお、実際にSQLを実行して計測する形式もあり、その場合はサーバーへの負荷に注意します。
CREATE INDEX idx_servers_environment_role
ON servers (environment, role);
EXPLAIN
SELECT hostname FROM servers
WHERE environment = 'prod' AND role = 'web';実行計画の出力例(製品によって列名や表示は異なります。架空の例であり、実機で実行した記録ではありません):
| id | select_type | table | type | key | rows |
|---|---|---|---|---|---|
| 1 | SIMPLE | servers | ref | idx_servers_environment_role | 12 |
typeがrefでkeyには、実際に使われたインデックスの名前が入ります。ここに作成したインデックス名が表示され、推定行数(rows)が表全体の行数より少なければ、インデックスが使われたと判断できます。面接では「実行計画を見て、インデックスが使われたことを確認した」と説明できます。typeがALLでkeyが空の場合は全件走査(フルスキャン)です。
- 遅いSQL、実行時間、頻度、対象時間帯をログで特定する。
EXPLAINで実行計画と推定件数を確認する。- 不要な列・行、関数、並び替え、結合条件を見直す。
- 必要なら候補インデックスを検証環境で比較する。
- 変更前後の時間、CPU、I/O、ロック、書き込み性能を記録する。
9. SQLキーワード早見表
| したいこと | 主なキーワード | 覚え方・注意 |
|---|---|---|
| 読む | SELECT ... FROM | 列を選び、表から読む |
| 絞る | WHERE | 変更系では対象漏れに注意 |
| 並べる | ORDER BY ... ASC/DESC | 昇順/降順 |
| 重複を除く | DISTINCT | 結果の重複を1つにする |
| まとめる | GROUP BY / HAVING | 集計後の条件はHAVING |
| つなぐ | JOIN ... ON | キーと件数を確認 |
| 追加 | INSERT INTO ... VALUES | 列と値の順番を合わせる |
| 更新 | UPDATE ... SET ... WHERE | 先に同条件でSELECT |
| 削除 | DELETE FROM ... WHERE | バックアップと切り戻し |
| 構造を作る | CREATE TABLE | 型と制約を設計 |
| 確定/取消 | COMMIT / ROLLBACK | 変更を一まとまりにする |
| 権限 | GRANT / REVOKE | 最小権限 |
| 計画を見る | EXPLAIN | 製品ごとの出力を読む |
10. サーバー運用でSQLエラーが起きたとき
- 影響を確認:誰が、いつから、どの画面・処理で失敗しているかを記録する。
- 接続を確認:データベースのサービスが動いているか、待受ポート(接続を受け付ける番号)、DNS(ホスト名からIPアドレスを調べる仕組み)、ファイアウォール(決めた条件で通信を許可・拒否する関所)、接続先の設定、TLS(通信の暗号化)、認証の順に確認する。
- エラーを保存:画面の表示だけで判断しない。発生時刻、SQLSTATE(SQLの標準エラーコード)、データベースのログ、アプリのログを採取する。パスワードなどの秘密情報は伏せる。
- 切り分け:構文、権限、ロック、容量、接続数、タイムアウト、実行計画、直前変更を順に確認する。
- 変更を管理:本番で思いつきのSQLを実行せず、承認、対象確認、バックアップ、切り戻し、作業記録をそろえる。
- 復旧後を確認:エラーが消えたことだけでなく、件数、整合性、監視、再発有無を確認し、手順へ反映する。
「接続できない」ときの代表例
DBサービス停止、ホスト名・ポート間違い、Firewall遮断、接続元制限、証明書期限、ユーザー停止、パスワード変更、接続数上限を確認します。
「遅い」ときの代表例
全件走査、インデックス不足、統計情報、ロック待ち、同時実行数、CPU・メモリ・ディスクI/O、ネットワーク、データ急増を確認します。
「更新できない」ときの代表例
権限、読み取り専用、制約違反、データ型、トランザクション、デッドロック、ディスク満杯、レプリカ接続を確認します。
11. 手を動かす演習
最初は本番データではなく、SQLiteなどの使い捨て学習環境で実行してください。各演習では「実行したSQL・予想した結果・実際に出た結果・気づいたこと」を記録します。記録が残っていれば、面接や報告のときに「何を試して、何が分かったのか」を自分の言葉で説明できます。
学習環境の用意の仕方は、主に次の2通りです。
- ブラウザで動くオンライン実行環境を使う:検索エンジンで「SQLite オンライン実行」などと調べると、インストール不要でSQLiteを試せる学習用サービスが見つかります。そこに下記のSQL文を貼り付けて実行します。
- 自分のPCに手元の環境を作る:PCにSQLite(
sqlite3コマンドなど)を用意し、同じSQL文をローカルで実行してテーブルを作ります。
いずれの方法でも、まず次のSQL文をそのまま実行してください。#defineで説明したテーブル定義(owners・servers)と、#sampleのサンプルデータを再現できます。
CREATE TABLE owners (
owner_id INTEGER PRIMARY KEY,
owner_name VARCHAR(50) NOT NULL
);
INSERT INTO owners (owner_id, owner_name) VALUES
(1, 'インフラ担当'),
(2, 'アプリ担当'),
(3, '監視担当');
CREATE TABLE servers (
server_id INTEGER PRIMARY KEY,
hostname VARCHAR(100) NOT NULL UNIQUE,
role VARCHAR(30) NOT NULL,
environment VARCHAR(10) NOT NULL CHECK (environment IN ('dev', 'stg', 'prod')),
owner_id INTEGER,
cpu_usage DECIMAL(5,2) DEFAULT 0,
last_checked_at TIMESTAMP,
FOREIGN KEY (owner_id) REFERENCES owners (owner_id)
);
INSERT INTO servers (server_id, hostname, role, environment, owner_id, cpu_usage, last_checked_at) VALUES
(101, 'web-01', 'web', 'prod', 1, 82.3, '2026-08-28 09:50:00'),
(102, 'web-02', 'web', 'prod', 2, 45.1, '2026-08-28 09:55:00'),
(103, 'db-01', 'db', 'prod', 1, 91.7, '2026-08-28 10:00:00'),
(104, 'db-02', 'db', 'dev', NULL, 15.2, '2026-08-20 08:00:00'),
(105, 'app-01', 'app', 'stg', 2, 66.4, '2026-08-27 22:10:00'),
(106, 'app-02', 'app', 'prod', NULL, 88.9, '2026-08-28 09:40:00'),
(107, 'batch-01', 'batch', 'dev', 3, 12.0, '2026-08-15 07:30:00'),
(108, 'cache-01', 'cache', 'prod', 3, 77.5, '2026-08-28 09:45:00');演習1:安全に読む
本番環境のサーバー名と役割を、サーバー名順で最大20件取得してください。
解答例
SELECT hostname, role
FROM servers
WHERE environment = 'prod'
ORDER BY hostname ASC
LIMIT 20;演習2:集計する
環境ごとのサーバー台数と平均CPU使用率を求めてください。
解答例
SELECT environment, COUNT(*) AS server_count,
AVG(cpu_usage) AS avg_cpu
FROM servers
GROUP BY environment;演習3:未設定を探す
LEFT JOIN を使い、担当者が設定されていないサーバーを探してください。
解答例
SELECT s.hostname
FROM servers AS s
LEFT JOIN owners AS o ON s.owner_id = o.owner_id
WHERE o.owner_id IS NULL;演習4:更新前確認
server_id 104の環境を変更する前に、対象を確認するSQLを書いてください。
解答例
SELECT server_id, hostname, environment
FROM servers
WHERE server_id = 104;演習5:失敗を調べる
「アプリからDBへ接続できない」場合の確認順を、接続先、ネットワーク、認証、ログの観点で説明してください。
確認観点の例
- 接続先:ホスト名・ポート番号・DB名・接続文字列に誤りがないかを確認する。
- ネットワーク:DNS解決、Firewall・セキュリティグループ、経路上の到達性を確認する。
- 認証:ユーザー名・パスワード、権限、証明書の期限、アカウントの有効期限を確認する。
- ログ:アプリ側のエラーメッセージ、DBサーバー側の接続ログ、直前の設定変更履歴を確認する。
演習6:説明する
WHERE と HAVING、DELETE と DROP の違いを、初学者へ1文ずつ説明してください。
模範解答例
- WHERE と HAVING:
WHEREは集計前の行を1行ずつ絞り込む条件で、HAVINGはGROUP BYでまとめた後のグループを絞り込む条件です。 - DELETE と DROP:
DELETEはテーブル構造を残したまま行(データ)だけを削除する操作で、DROP TABLEはテーブルの構造自体をデータごと削除する操作です。