はじめに
フロントエンドを書いていると、こんな感覚になることはないでしょうか。
- APIは叩ける
- レスポンスの型も分かる
- でも、その中身は正直よく分からない
SQLやDB設計は「バックエンドの人が考えるもの」と割り切ってきた人も多いと思います。
ただ、画面に表示されているデータは、すべてSQLの結果です。
全部を書けるようになる必要はありません。でも「想像できる」ようになるだけで、フロントエンジニアとしての見える世界は変わります。
フロントとSQLの距離は、思っているより近い
フロントエンジニアが普段見ている世界を、構造として整理するとこうなります。
フロントから見ると、触っているのはAPIまでですが、その先では必ずSQLが動いています。
画面の仕様は、そのままSQLの仕様です。
「APIが重い」の正体は、SQLであることが多い
フロント側でよくある会話です。
「この画面、ちょっと重いですね」
「APIが遅いみたいです」
SQLが少し読めると、次のような視点が持てるようになります。
- 毎回複数テーブルをJOINしていないか?
- 集計を都度やっていないか?
- 本当にこの件数が必要か?
「なんとなく重い」から、理由を考えられるようになるのが大きな違いです。
そもそもDB設計とは何か
DB設計の本質は、とてもシンプルです。
どんな情報を、どう分けて、どう繋げて持つかを決めること
❌ 悪い例:全部入りテーブル
一見シンプルに見えますが、以下の問題が出やすくなります。
- 情報が重複する:同じユーザーが複数回注文すると、名前・住所が何度も保存される
- 修正に弱い:ユーザーの住所変更時、過去の注文すべてを更新する必要がある
- SQLが複雑になる:検索や集計の条件が書きづらい
結果として、フロントに返ってくるAPIも扱いづらくなりがちです。
⭕ 良い例:役割ごとに分ける
メリット:
- 情報の意味ごとにテーブルを分ける
- ID(主キー/外部キー)で繋ぐ
- 変更に強く、SQLも素直になる
DB設計は、未来の変更に耐えるための準備です。
JOINは「画面を組み立てる作業」
JOINは魔法ではありません。複数のテーブルを使って、画面に必要な情報を組み立てる作業です。
JOINとは?
例えば、ECサイトの注文一覧画面を思い浮かべてください。
画面に表示したい情報:
- 注文ID
- 注文日
- ユーザー名(← ユーザーテーブルにある)
- 商品名(← 商品テーブルにある)
- 数量
これらの情報は、1つのテーブルには全部入っていません。
JOINは、このバラバラのテーブルを「IDをキーに繋げて」、画面に必要な形にまとめる作業です。
フロント開発でのイメージ
これは、フロントでやっている処理と似ています:
// フロントでもこんなコードを書きますよね?
const orderWithUser = orders.map(order => {
const user = users.find(u => u.id === order.userId);
return {
...order,
userName: user?.name
};
});
SQLのJOINは、これをデータベース側でやっているだけです。
テーブル間の繋がり
それでは、実際のSQLでJOINを見ていきましょう。
SQL例① 注文一覧(ユーザー名つき)
「一覧画面でよくある、いちばん基本の形」です。
目的
一覧画面で「誰が・いつ・どの注文をしたか」を表示する
フロント目線のポイント
- 一覧の1行は「注文」単位
- 表示用のユーザー名はJOINで取得する
SELECT
o.order_id,
o.order_date,
u.name AS user_name
FROM "ORDER" o
JOIN "USER" u
ON o.user_id = u.user_id -- IDで結合するのが基本
ORDER BY
o.order_date DESC;
実行結果イメージ:
| order_id | order_date | user_name |
|---|---|---|
| 103 | 2024-12-15 | 田中太郎 |
| 102 | 2024-12-14 | 佐藤花子 |
| 101 | 2024-12-13 | 田中太郎 |
SQL例② 商品名まで含めた一覧(JOINが増える例)
目的
注文一覧に「商品名」「数量」も表示したい
フロント目線のポイント
- 注文1件に複数商品があると、行数が増える
- 画面の行数が意図通りかを必ず確認する
SELECT
o.order_id,
o.order_date,
u.name AS user_name,
p.name AS product_name,
od.quantity
FROM "ORDER" o
JOIN "USER" u
ON o.user_id = u.user_id
JOIN ORDER_DETAIL od
ON o.order_id = od.order_id
JOIN PRODUCT p
ON od.product_id = p.product_id
ORDER BY
o.order_date DESC;
実行結果イメージ:
| order_id | order_date | user_name | product_name | quantity |
|---|---|---|---|---|
| 103 | 2024-12-15 | 田中太郎 | ノートPC | 1 |
| 103 | 2024-12-15 | 田中太郎 | マウス | 2 |
| 102 | 2024-12-14 | 佐藤花子 | キーボード | 1 |
⚠️ 注意:注文103は商品が2つあるため、2行に分かれています。
SQL例③ 「1注文を1行」にまとめる(集約)
目的
注文1件を1行にして、点数や合計金額を表示する
フロント目線のポイント
- 1行にまとめたい場合は集約(
GROUP BY)が必要 - 「何を1行にするか」を先に決める
SELECT
o.order_id,
o.order_date,
u.name AS user_name,
COUNT(od.product_id) AS item_count,
SUM(p.price * od.quantity) AS total_amount
FROM "ORDER" o
JOIN "USER" u
ON o.user_id = u.user_id
JOIN ORDER_DETAIL od
ON o.order_id = od.order_id
JOIN PRODUCT p
ON od.product_id = p.product_id
GROUP BY
o.order_id, o.order_date, u.name
ORDER BY
o.order_date DESC;
実行結果イメージ:
| order_id | order_date | user_name | item_count | total_amount |
|---|---|---|---|---|
| 103 | 2024-12-15 | 田中太郎 | 2 | 152,000円 |
| 102 | 2024-12-14 | 佐藤花子 | 1 | 8,000円 |
| 101 | 2024-12-13 | 田中太郎 | 3 | 45,000円 |
✅ 結果:注文ごとに1行になり、商品数と合計金額が集計されています。
フロントエンジニアがSQLを少し知っていると、何が変わるか
- APIが重い理由を想像できる
- 無理な画面仕様に気づける
- バックエンドとの会話が具体的になる
全部を書ける必要はありません。読める・説明できる、それだけで、仕事の質が確実に変わります。
AI時代でもSQLと設計が大事な理由
最近はAIがSQLを書いてくれる時代になりました。
便利ではありますが、そのSQLが正しいかどうかを判断するのは人間です。
SQLが読めると、AIを「そのまま使う側」ではなく、確認して使える側になります。
まとめ
- フロントの画面は、SQLの結果でできている
- SQLとDB設計を少し知るだけで、判断できることが増える
- 全部書けなくていい、読めればいい
SQLは、フロントエンジニアにとって遠い技術ではなく、すぐ隣にある技術です。
まずは、次にAPIを叩くときに「裏側でどんなSQLが動いているんだろう?」と想像してみてください。それだけで、見える世界が少し変わるはずです。
オブジェクティブグループでは X の投稿も平日毎日行っています!
IT 関連の小ネタや便利技から、日常のアニメ・ゲーム布教なども幅広く投稿してるので、ご興味のある方は是非フォロー・いいねをお願いします。