CURRICULUM / LAYER 01 / CHAPTER 06
データベースとSQLを読む
ウェブサービスを支えているのは、画面でもコードでもなく、データです。
そのデータはどこに住んでいるのか。
どう呼び出されるのか。
ここが見えると、ウェブ全体の仕組みが一段立体的になります。
データはどこにあるのか
あなたがTwitterで投稿したツイート、Amazonで買った商品の履歴、LINEで送ったメッセージ。
これらはすべて、サーバーのどこかに保管されています。
PHPが動いているサーバーの「隣の部屋」のようなところに、専用のスペースがあって、そこに行儀よく並んでいるのです。
その保管場所をデータベース(Database、略してDB)と呼びます。
もう少し正確に言うと、データを整理して保管し、必要なときに素早く取り出すための専用ソフトウェアのことです。
代表的なものに MySQL、PostgreSQL、SQLite などがあり、このサイトや世の中の多くのウェブサービスで使われています。
ウェブサービスの全体像は、こう整理できます。
HTML/CSS/JS がブラウザで見た目と動きを作り、
PHP がサーバーで処理を行い、
データベース がそのデータを保管する。
この3者の連携で、あらゆるウェブサービスは成り立っています。
データベースは「巨大なスプレッドシート」だと思ってOK
データベースというと何やら難しそうに聞こえますが、入門段階ではシンプルに考えてOKです。
「たくさんの行と列からなる、Excelのような表がいくつも並んでいるもの」。
この理解で十分スタートできます。
たとえば、このCodeReaderというサイトが将来、学習者アカウントを持つようになったとしましょう。
そのとき、データベースの中にはこんな表(テーブルと呼びます)が作られるはずです。
users テーブル
+----+-----------+-------------------+---------------------+
| id | name | email | created_at |
+----+-----------+-------------------+---------------------+
| 1 | 山田太郎 | yamada@example.jp | 2026-04-01 10:23:00 |
| 2 | 田中花子 | tanaka@example.jp | 2026-04-02 14:51:00 |
| 3 | 佐藤一郎 | sato@example.jp | 2026-04-05 09:12:00 |
+----+-----------+-------------------+---------------------+
横の並び(1行)が1人のユーザーを表し、縦の並び(1列)がそのユーザーのある属性(id、名前、メールアドレス…)を表します。
縦の列のことをカラム(column)、横の行のことをレコード(record)や行(row)と呼びます。
テーブルは複数あってよく、それぞれが別の種類のデータを保管します。
usersテーブルの他に、posts(投稿)テーブル、comments(コメント)テーブル、などがあって、それらが互いに関係を持ちながらデータ全体を構成する。
これがリレーショナルデータベースという考え方です。
SQL ─ データベースとの会話の言葉
では、この表から「山田太郎さんのメールアドレスを取り出したい」と思ったとき、どうすればいいでしょうか。
ここで登場するのが、データベース専用の言葉、SQL(エスキューエル、または「シークェル」と読みます)です。
SQLの面白いところは、ほとんど英語の文章のように読めることです。
さっきの問いへの答えは、こう書きます。
SELECT email FROM users WHERE name = '山田太郎';
読み下すとこうです。
「emailを選んで(SELECT email)、usersテーブルから(FROM users)、nameが山田太郎である条件で(WHERE name = '山田太郎')」。
ほぼ英文のまま意味が通じますね。
SQLは「人間が読みやすいように」意図的に英語っぽく設計された言語です。
だからこそ、セレクタを覚えるより、プロパティを覚えるより、ずっと素直に読めます。
深く理解するのは後回しで、まずは「読める」状態を目指しましょう。
SQLの基本4動詞 ─ SELECT, INSERT, UPDATE, DELETE
SQLには100以上の命令がありますが、実務で頻繁に使うのは4つだけです。
この4つは「CRUD」(クラッド)と総称されます。
Create(作る)・Read(読む)・Update(更新する)・Delete(削除する)の頭文字です。
1. SELECT ─ データを読み取る
一番よく使う命令です。
条件に合うデータを取り出します。
-- usersテーブルからすべての行を取る SELECT * FROM users; -- nameとemailの列だけを取る SELECT name, email FROM users; -- idが3のユーザーを取る SELECT * FROM users WHERE id = 3; -- 新しく登録された順に5件取る SELECT * FROM users ORDER BY created_at DESC LIMIT 5;
主なキーワードを整理しておきます。
*── 「すべての列」を意味する記号WHERE── 条件を指定する(「〜である」の部分)ORDER BY── 並び順を指定する(ASC=昇順、DESC=降順)LIMIT── 取得する件数の上限を指定する
2. INSERT ─ 新しいデータを追加する
-- 新しいユーザーを追加する INSERT INTO users (name, email) VALUES ('鈴木健太', 'suzuki@example.jp');
「usersテーブルに、nameとemailの列へ値を入れる」という読み方です。
idとcreated_atは指定していませんが、これらはデータベース側で自動的に付けてくれるよう設定されていることが多いです(idは自動連番、created_atは現在時刻)。
3. UPDATE ─ 既存のデータを書き換える
-- idが2のユーザーのメールアドレスを変更する UPDATE users SET email = 'new_address@example.jp' WHERE id = 2;
UPDATE文で WHERE を書き忘れると、テーブルの全行が書き換わります。
「テーブルの全ユーザーのメールアドレスが、たった1つの値に上書きされる」という惨事が起こり得ます。
UPDATEを書くときは、必ず WHERE を先に書いてから、本体を書く習慣を付けましょう。
4. DELETE ─ データを削除する
-- idが3のユーザーを削除する DELETE FROM users WHERE id = 3;
DELETE文も、WHERE を忘れるとテーブルのすべての行が消えます。
UPDATE以上に取り返しがつかない事故になりえます。
実務では、いきなりDELETEを打つ前に、同じ条件でまずSELECTして「消す対象が本当に合っているか」を確認するのが鉄則です。
PHPからSQLを使う ─ ただし、危険な書き方がある
ウェブサイトでは、人間が手でSQLを打つのではなく、PHPがSQLを組み立ててデータベースに送ります。
たとえば、ログインフォームから送られてきたユーザー名でデータベースを検索する処理は、こんなふうに書きたくなるかもしれません。
<?php // ⚠️ これは危険な書き方の例です。真似しないでください $username = $_POST['username']; $sql = "SELECT * FROM users WHERE name = '" . $username . "'"; $result = $db->query($sql); ?>
一見、何の問題もなさそうに見えます。
動きます。
普通のユーザー名なら、ちゃんと検索できます。
でも、このコードにはウェブの歴史で最も有名な脆弱性が潜んでいます。
それがSQLインジェクション(SQL Injection)です。
もし悪意のあるユーザーが、ユーザー名の欄に次のような文字列を入力したらどうなるでしょうか。
' OR '1'='1
すると、PHPが組み立てるSQL文は、こうなります。
SELECT * FROM users WHERE name = '' OR '1'='1'
読み下してみてください。
「nameが空である、または、'1'が'1'に等しい条件で」。
'1'は必ず'1'に等しいので、この条件は常に真になります。
結果として、全ユーザーの情報がごっそり返ってきます。
もっと悪質な攻撃では、ユーザー入力に「'; DROP TABLE users; --」のような文字列を混ぜることで、テーブルをまるごと削除することすらできます。
これが、20年以上もウェブ開発者を悩ませ続けている、SQLインジェクションという攻撃の正体です。
SQLインジェクションは、ユーザー入力をSQL文に文字列として連結するという、たった一つの習慣から生まれます。
これを防ぐには「プリペアドステートメント」という仕組みを使う必要がありますが、詳しくはLayer 02のSQLインジェクション章で徹底的にやります。
今のうちに心に刻んでおいてほしいのは、「$変数 を直接SQL文に連結するコードを見たら、脆弱性を疑え」ということだけです。
正しい書き方(予告編)
Layer 02で詳しくやりますが、正しく書くとこうなります。
今はじっくり読まなくても大丈夫です。
「こういう書き方があるらしい」とだけ覚えておいてください。
<?php // ✓ 安全な書き方(プリペアドステートメント) $username = $_POST['username']; $stmt = $db->prepare("SELECT * FROM users WHERE name = ?"); $stmt->execute([$username]); $user = $stmt->fetch(); ?>
ポイントは、SQLの中に変数を直接書いていないこと。
代わりに ? という「プレースホルダ」を置いて、後から値を渡しています。
こうすると、データベースが「SQL本体」と「ユーザーが渡した値」をきっちり区別してくれるため、値の中に悪意のあるSQL文が混じっていても、それはあくまで「文字列」として扱われ、SQL文として実行されることはありません。
解読演習
このSQLは何をするか、日本語で説明してください
次のSQLを読んで、それぞれ何をしているか、自分の言葉で説明してみてください。
英語っぽく読めば意味は取れるはずです。
-- ① SELECT name FROM users WHERE id = 5; -- ② SELECT * FROM posts ORDER BY created_at DESC LIMIT 10; -- ③ UPDATE users SET email = 'new@example.jp' WHERE id = 1; -- ④ INSERT INTO comments (post_id, body) VALUES (42, '面白い記事でした');
解答例を見る
① usersテーブルから、idが5であるユーザーのname(名前)を取得する。
1人だけが返ってくるはずです(idは重複しない前提なので)。
② postsテーブルから、created_atの降順(新しい順)で、最初の10件をすべての列で取得する。
「最新の投稿10件」を取るときの定番パターンです。
③ usersテーブルの、idが1のユーザーのemail列を 'new@example.jp' に書き換える。
WHEREで1人に絞っているので安全ですが、もしWHEREを書き忘れていたら全ユーザーが書き換わる恐ろしいSQLになります。
④ commentsテーブルに新しい行を追加する。
post_id列には42、body列には '面白い記事でした' を入れる。
つまり、id=42の投稿に対する新しいコメントを登録するSQLです。
このSQLは、なぜ危険か
AIが書いてきたログイン処理のPHPコードです。
このコードには、本章で説明した脆弱性があります。
どこがどう危険か、また、悪意のあるユーザーがどんな文字列を入力すると何が起きるか、説明してみてください。
<?php $user = $_POST['username']; $pass = $_POST['password']; $sql = "SELECT * FROM users WHERE name = '" . $user . "' AND password = '" . $pass . "'"; $result = $db->query($sql); if ($result->num_rows > 0) { // ログイン成功とみなす } ?>
解答例を見る
このコードには、典型的なSQLインジェクション脆弱性があります。
ユーザー入力を文字列として直接SQL文に連結しているからです。
たとえば、ユーザー名の欄に admin' -- と入力したとします(-- はSQLのコメント開始記号)。
すると組み立てられるSQL文は、こうなります:
SELECT * FROM users WHERE name = 'admin' -- ' AND password = ''
-- 以降は全部コメント扱いになるので、password のチェックが丸ごと無効化されます。
パスワードを知らなくても、adminとしてログインできてしまうわけです。
これは実際に過去多くのサイトで起きた事故です。
さらに悪質な攻撃なら、' OR 1=1 -- と入力することで「すべての行が返る」状態を作り、条件なしで「ログイン成功」と判定させることもできます。
他にも、このコードには「パスワードを平文で保存・比較している」という、もう一つの重大な問題もあります(パスワードは必ずハッシュ化して保存するのが鉄則です)。
これはLayer 02の「パスワードと認証」の章で扱います。
SQLインジェクションを防ぐには、プリペアドステートメントを使います。
今は「ユーザー入力と.で連結してSQLを作るコードを見たら即警戒」という眼だけ養えればOKです。
このテーブル設計、どこが微妙?
次はSQLではなく、テーブル設計の話です。
ある人が「ブログ記事とコメントを保存するテーブル」を設計しました。
一見動きそうですが、よく考えると不便な点があります。
どこが微妙でしょうか?
posts テーブル
+----+----------+----------+---------------------------+
| id | title | body | comments |
+----+----------+----------+---------------------------+
| 1 | 初投稿 | 本文... | 山田:いいね / 田中:最高! |
| 2 | 2本目 | 本文... | 佐藤:続き楽しみ |
| 3 | 3本目 | 本文... | |
+----+----------+----------+---------------------------+解答例を見る
この設計の問題点は、コメントを1つの文字列として1つのセルに詰め込んでいることです。
一見コンパクトに見えますが、実用上いろいろと困ります。
(1) 特定のコメントだけを取り出せない ── 「山田さんのコメントだけ見たい」と思っても、文字列をプログラムで分解しないと取れません。
SQLの強みが活かせなくなります。
(2) コメントの追加・削除が困難 ── 新しいコメントを追加するたびに、元の文字列を取ってきて、後ろに継ぎ足して、書き戻す、という面倒な処理が必要です。
DELETEはもっと面倒です。
(3) コメントごとの情報(投稿日時、投稿者ID、いいね数など)を持てない ── 1つのセルに文字列で入っているだけなので、各コメントに詳細な属性を持たせられません。
正しい設計は、コメントは別テーブルに分けることです。
こんなふうに:
posts テーブル comments テーブル +----+---------+ +----+---------+--------+----------+ | id | title | | id | post_id | author | body | +----+---------+ +----+---------+--------+----------+ | 1 | 初投稿 | | 1 | 1 | 山田 | いいね | | 2 | 2本目 | | 2 | 1 | 田中 | 最高! | | 3 | 3本目 | | 3 | 2 | 佐藤 | 続き… | +----+---------+ +----+---------+--------+----------+
post_id という列がcommentsテーブルにあり、「このコメントは、postsテーブルのどの行に属するか」を示しています。
これがリレーショナル(関係のある)データベースの本質です。
この設計なら、「id=1の投稿についているコメントをすべて取る」は SELECT * FROM comments WHERE post_id = 1; の一行で済みますし、コメント1つずつに日時や投稿者情報を自由に持たせられます。
テーブル設計は、初心者には難しく感じますが、「関連する情報はテーブルを分けて、IDで繋ぐ」という基本を押さえておくだけで、多くのケースに対応できます。
本格的な設計論はLayer 03で扱います。
この章のまとめ
- ウェブサービスのデータは、サーバーの中のデータベースという専用ソフトが管理している
- データベースは「行と列からなる表(テーブル)の集まり」として理解できる
- データベースとやり取りするための専用言語がSQL。英語のように読めるよう設計されている
- SQLの基本4動詞は SELECT / INSERT / UPDATE / DELETE(CRUD)
- UPDATEとDELETEでは WHERE を絶対に忘れない。全行が書き換わる/消える
- ユーザー入力を
.でSQLに連結する書き方はSQLインジェクションの温床。Layer 02で徹底的に扱う - 関連する情報は別テーブルに分けて、IDで繋ぐのがリレーショナルDBの基本
Layer 01 を終えて ─ あなたはもう「読める」人です
ここまでで、Layer 01「読める」の6章をすべて読み終えました。
おつかれさまでした。
最初の章でブラウザとサーバーの会話の話から始めて、HTML(意味のラベル)、CSS(見た目の装飾)、JavaScript(ブラウザでの動き)、PHP(サーバーでの処理)、そしてデータベースとSQL(データの保管と呼び出し)まで、ウェブを構成する主要な登場人物を一通り眺めてきました。
大事なのは、すべてを完璧に覚えることではありません。
AIが書いてきたコードや、他人のコードを渡されたときに、「このコードはざっくり何をしているか」を自分の言葉で説明できる状態。
それがLayer 01のゴールです。
そこまで来れば、あなたはもう「読める」人です。
次のLayer 02では、いよいよ「気付ける」の訓練に入ります。
読めるだけでは足りません。
そのコードが安全かどうか、罠が仕掛けられていないかを見抜けるようになること。
この章で何度か予告した「SQLインジェクション」「XSS」「認証の不備」といったテーマを、一つずつ丁寧に扱っていきます。
ここから先が、AI時代のエンジニアにとって最も重要なスキルセットです。