1. はじめに
前回は、正規化について学びました。
データの重複を減らし、整合性を保つ設計ができるようになりましたね!🎉
でも、こんな悩みはありませんか?🤔
- ユーザーをメールアドレスで検索
SELECT * FROM users WHERE email = 'tanaka@example.com';
データが少ない時は一瞬で返ってくるけど...
ユーザーが100万人になったら...🐢💦
10秒かかる...遅い!😱
「データが増えたら、クエリが遅くなった...」
「何とか高速化できないの?」
そんな悩みを解決するのが、今回学ぶインデックス(Index) です!✨
1.1 前回のおさらい📝
- 第1正規形 - 繰り返し項目をなくす
- 第2正規形 - 部分関数従属をなくす
- 第3正規形 - 推移的関数従属をなくす
今回は、正規化されたテーブルを高速化する技術を学びます!
2. インデックス(Index)とは?🔍
インデックスとは、データベースの検索を高速化するための仕組みです。
2.1 日常生活で例えると📚
📖 辞書や教科書の「索引(さくいん)」
想像してください...500ページの教科書で「データベース」という単語を探すとき:
❌ 索引がない場合
1ページ目:「データベース」は...ない
2ページ目:「データベース」は...ない
3ページ目:「データベース」は...ない
...
...
287ページ目:あった!😅
時間かかりすぎ!🕐💦
✅ 索引がある場合
巻末の索引を見る
↓
「データベース...287ページ」
↓
287ページを直接開く!✨
一瞬で見つかる!🚀
データベースのインデックスも、これと同じ仕組みです!
🏢 図書館の「目録カード」
❌ 目録なし:
全ての本棚を見て回る...疲れる😰
✅ 目録あり:
タイトルで検索 → 棚番号がわかる → 直行!🎯
2.2 データベースでの例💾
❌ インデックスなし
-- 100万人のユーザーから検索
SELECT * FROM users WHERE email = 'tanaka@example.com';
-
データベースの動き
1人目: tanaka@...? → 違う
2人目: sato@...? → 違う
3人目: suzuki@...? → 違う
...
...
500,000人目: tanaka@...? → あった!😅
フルスキャン(全件走査)!遅い!🐢
✅ インデックスあり
-- emailカラムにインデックスを作成
CREATE INDEX idx_email ON users(email);
-- 検索
SELECT * FROM users WHERE email = 'tanaka@example.com';
-
データベースの動き
インデックスを見る
↓
'tanaka@...' の位置がわかる
↓
その位置のデータを直接取得!✨
一瞬で見つかる!🚀
2.3 インデックスの効果📊
| 項目 | インデックスなし | インデックスあり |
|---|---|---|
| 検索方法 | 全件走査(フルスキャン) | インデックスから直接アクセス |
| 速度(10万件) | 数秒〜数十秒🐢 | 0.01秒以下⚡ |
| 速度(100万件) | 数十秒〜数分🐌 | 0.01秒以下⚡ |
| メリット | ストレージ節約 | 検索が超高速 |
| デメリット | 遅い | ストレージ使用、更新が少し遅い |
データが増えるほど、インデックスの効果は絶大!🎉
3. インデックスの仕組み🔧
3.1 インデックスはどうやって速くするの?🤔
データ構造:B-Tree(ビーツリー)
ほとんどのデータベースは、B-Treeという木構造でインデックスを管理します。
図解:B-Treeインデックス🌳
[M]
/ \
[F] [T]
/ \ / \
[A-E][G-L] [N-S][U-Z]
↓ ↓ ↓ ↓
データ データ データ データ
検索の流れ
'tanaka@example.com' を検索する場合:
1. ルートノード[M]を見る
't' > 'm' なので右へ→
2. [T]を見る
't' = 't' なので、この下を探す
3. [U-Z]ではなく、'T'で始まるグループへ
→ 'tanaka@...'を発見!🎯
たった3ステップ!速い!⚡
フルスキャンとの比較
フルスキャン(インデックスなし)
ユーザー1: aaa@...
ユーザー2: bbb@...
ユーザー3: ccc@...
...
ユーザー500,000: tanaka@... ← やっと見つかった!
最悪、全件チェックが必要なことも!😱
インデックススキャン
B-Treeで探索
→ ログ時間 O(log N) で見つかる!
100万件でも、約20ステップで見つかる!🚀
3.2 計算量の比較📈
| データ件数 | フルスキャン | インデックス(B-Tree) |
|---|---|---|
| 100件 | 100回 | 約7回 |
| 1,000件 | 1,000回 | 約10回 |
| 10,000件 | 10,000回 | 約13回 |
| 100,000件 | 100,000回 | 約17回 |
| 1,000,000件 | 1,000,000回 | 約20回 |
データが増えても、インデックスなら検索回数はほとんど増えない!✨
4. インデックスの作成方法🔨
4.1 基本構文📝
-- 単一カラムのインデックス
CREATE INDEX インデックス名 ON テーブル名(カラム名);
-- 複数カラムのインデックス(複合インデックス)
CREATE INDEX インデックス名 ON テーブル名(カラム1, カラム2);
-- ユニークインデックス(値の重複を許さない)
CREATE UNIQUE INDEX インデックス名 ON テーブル名(カラム名);
4.2 実例1:単一カラムのインデックス🎯
usersテーブル
CREATE TABLE users (
user_id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL,
email VARCHAR(255) NOT NULL,
age INT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
emailで検索することが多い場合
-- emailカラムにインデックスを作成
CREATE INDEX idx_email ON users(email);
これで、以下のクエリが高速になります!
-- メールアドレスで検索
SELECT * FROM users WHERE email = 'tanaka@example.com';
-- 速い!⚡
-- メールアドレスの部分一致
SELECT * FROM users WHERE email LIKE 'tanaka%';
-- これも速い!⚡
-- ただし、後方一致は遅い
SELECT * FROM users WHERE email LIKE '%example.com';
-- インデックスが効かない😢
4.3 実例2:複合インデックス🧩
ordersテーブル
CREATE TABLE orders (
order_id INT AUTO_INCREMENT PRIMARY KEY,
customer_id INT NOT NULL,
order_date DATE NOT NULL,
status VARCHAR(20),
total_amount DECIMAL(10,2)
);
customer_idとorder_dateで検索することが多い場合
-- 複合インデックスを作成
CREATE INDEX idx_customer_date ON orders(customer_id, order_date);
複合インデックスが効くクエリ✅
-- パターン1:両方指定(最速!⚡⚡)
SELECT * FROM orders
WHERE customer_id = 1 AND order_date = '2026-01-15';
-- パターン2:先頭カラムだけ(速い!⚡)
SELECT * FROM orders WHERE customer_id = 1;
-- パターン3:範囲検索も可能
SELECT * FROM orders
WHERE customer_id = 1
AND order_date BETWEEN '2026-01-01' AND '2026-01-31';
複合インデックスが効かないクエリ❌
-- order_dateだけで検索(効かない!😢)
SELECT * FROM orders WHERE order_date = '2026-01-15';
理由:
複合インデックス (customer_id, order_date) は、
「customer_idでソート → 同じcustomer_id内でorder_dateでソート」
という構造なので、order_dateだけでは検索できない!
図解:複合インデックスの構造
(customer_id, order_date)
customer_id=1, order_date=2026-01-10
customer_id=1, order_date=2026-01-15 ← customer_id=1の中でソート済み
customer_id=1, order_date=2026-01-20
customer_id=2, order_date=2026-01-05
customer_id=2, order_date=2026-01-12
...
「customer_idから始まる」検索は速い✅
「order_dateだけ」だと、全体を見ないといけない❌
4.4 実例3:ユニークインデックス🔒
-- emailの重複を許さない + インデックス
CREATE UNIQUE INDEX idx_unique_email ON users(email);
-- これで、以下が保証される
INSERT INTO users (username, email) VALUES ('田中', 'tanaka@example.com');
-- OK✅
INSERT INTO users (username, email) VALUES ('佐藤', 'tanaka@example.com');
-- エラー!重複は許されない!❌
ユニークインデックスは、インデックス + UNIQUE制約の両方の効果!
5. インデックスが効く場面・効かない場面⚖️
5.1 ✅ インデックスが効く場合
1. WHERE句での等価検索
SELECT * FROM users WHERE user_id = 100;
SELECT * FROM users WHERE email = 'tanaka@example.com';
2. WHERE句での範囲検索
SELECT * FROM orders WHERE order_date >= '2026-01-01';
SELECT * FROM products WHERE price BETWEEN 1000 AND 5000;
3. ORDER BYでの並び替え
-- ageにインデックスがあれば高速
SELECT * FROM users ORDER BY age DESC;
4. JOIN句での結合
-- customer_idにインデックスがあれば高速
SELECT *
FROM orders o
INNER JOIN customers c ON o.customer_id = c.customer_id;
5. 前方一致検索
-- emailにインデックスがあれば高速
SELECT * FROM users WHERE email LIKE 'tanaka%';
5.2 ❌ インデックスが効かない場合
1. 後方一致・中間一致検索
-- インデックスが効かない!😢
SELECT * FROM users WHERE email LIKE '%example.com';
SELECT * FROM users WHERE email LIKE '%tanaka%';
理由:
インデックスは「先頭から」ソートされているので、
後方一致は検索できない!
2. カラムに関数を適用
-- インデックスが効かない!😢
SELECT * FROM users WHERE YEAR(created_at) = 2026;
SELECT * FROM users WHERE UPPER(email) = 'TANAKA@EXAMPLE.COM';
理由:
関数を適用すると、インデックスの値と異なる値になるため!
解決策:
-- こうすれば効く!✅
SELECT * FROM users
WHERE created_at >= '2026-01-01'
AND created_at < '2027-01-01';
3. NOT、!=、<>を使った否定
-- インデックスが効きにくい
SELECT * FROM users WHERE status != 'active';
SELECT * FROM users WHERE age <> 30;
理由:
「○○以外」は、ほとんどのデータが該当する可能性があり、
フルスキャンの方が速いことが多い。
4. ORを使った複数条件
-- インデックスが効きにくい
SELECT * FROM users WHERE email = 'tanaka@example.com' OR age = 25;
解決策:
UNION を使う
SELECT * FROM users WHERE email = 'tanaka@example.com'
UNION
SELECT * FROM users WHERE age = 25;
5. データの選択率が高い場合
-- 該当データが全体の30%以上ある場合、フルスキャンの方が速いことも
SELECT * FROM users WHERE age >= 20; -- ほとんどのユーザーが該当
理由:
インデックスを使うと、
「インデックスを見る → データを取りに行く」の2段階になる。
該当データが多いと、何度もデータを取りに行くことになり、
かえって遅くなる!
6. 複合インデックスの順序が重要!📏
6.1 カラムの順序で効果が変わる!
シナリオ
以下のクエリをよく実行する:
SELECT * FROM orders WHERE customer_id = 1 AND status = 'shipped';
パターン1:(customer_id, status)
CREATE INDEX idx_customer_status ON orders(customer_id, status);
✅ このクエリは高速!
SELECT * FROM orders WHERE customer_id = 1 AND status = 'shipped';
✅ これも高速!
SELECT * FROM orders WHERE customer_id = 1;
❌ これは遅い!
SELECT * FROM orders WHERE status = 'shipped';
パターン2:(status, customer_id)
CREATE INDEX idx_status_customer ON orders(status, customer_id);
✅ このクエリは高速!
SELECT * FROM orders WHERE customer_id = 1 AND status = 'shipped';
✅ これも高速!
SELECT * FROM orders WHERE status = 'shipped';
❌ これは遅い!
SELECT * FROM orders WHERE customer_id = 1;
6.2 どちらを先にすべき?🤔
基本原則
- 選択性が高いカラムを先に(絞り込める方)
- よく単独で検索されるカラムを先に
- 等価検索を範囲検索より先に
例:選択性を考える
- customer_id: 1000種類の値がある(選択性:高)
- status: 5種類の値しかない。('pending','shipped','delivered','cancelled','returned')
(選択性:低)
この場合、customer_idを先にすべき!
CREATE INDEX idx_customer_status ON orders(customer_id, status);
理由:
customer_id で絞り込む → 1000分の1に減る!🎯
↓
status で絞り込む → さらに5分の1に減る
逆だと...
status で絞り込む → 5分の1に減る(まだ多い)
↓
customer_id で絞り込む → やっと減る
7. カバリングインデックス(Covering Index)🎁
7.1 さらなる高速化テクニック!
カバリングインデックスとは、
クエリで必要な全てのカラムがインデックスに含まれている状態のこと。
通常のインデックス
CREATE INDEX idx_email ON users(email);
SELECT user_id, username, email FROM users WHERE email = 'tanaka@example.com';
動き:
1. インデックスから email='tanaka@...' の位置を見つける
2. その位置のデータ(user_id, username, email)をテーブルから取得
↑
これが少し時間かかる
カバリングインデックス
CREATE INDEX idx_email_covering ON users(email, user_id, username);
SELECT user_id, username, email FROM users WHERE email = 'tanaka@example.com';
動き:
1. インデックスから email='tanaka@...' を見つける
2. インデックスに user_id, username も含まれている!
→ テーブルにアクセスせずに、インデックスだけで完結!⚡⚡
さらに速い!🚀
注意点⚠️
- インデックスのサイズが大きくなる💾
- 更新が少し遅くなる
- 本当に必要な場合だけ使う
8. インデックスのデメリット📉
8.1 良いことばかりではない!
1. ストレージを使う💾
データテーブル: 1GB
インデックス: 200MB (約20%)
インデックスが増えると、ディスク容量を圧迫!
2. 更新が遅くなる🐢
INSERT INTO users (username, email) VALUES ('田中', 'tanaka@example.com');
インデックスなし:
1. データを追加(1回の書き込み)
インデックスあり:
1. データを追加
2. emailインデックスを更新
3. usernameインデックスを更新(もしあれば)
4. ...
更新箇所が増える!少し遅くなる😢
3. インデックスの保守が必要🔧
データが追加・削除されると、
インデックスが断片化(フラグメンテーション)する
定期的にインデックスの再構築が必要:
ALTER TABLE users REBUILD INDEX;
9. インデックスの確認方法🔍
9.1 どのインデックスがあるか確認
MySQL
-- テーブルのインデックス一覧
SHOW INDEX FROM users;
結果:
| Table | Key_name | Column_name | Index_type |
|-------|-----------|-------------|------------|
| users | PRIMARY | user_id | BTREE |
| users | idx_email | email | BTREE |
PostgreSQL
-- テーブルのインデックス一覧
\d users
または
SELECT * FROM pg_indexes WHERE tablename = 'users';
9.2 EXPLAINでクエリの実行計画を確認🔎
EXPLAINを使うと、クエリがインデックスを使っているか確認できます!
MySQL
EXPLAIN SELECT * FROM users WHERE email = 'tanaka@example.com';
結果:
| type | key | rows | Extra |
|------|-----------|------|-----------------------|
| ref | idx_email | 1 | Using where |
読み方:
-
type: ref→ インデックスを使っている!✅ -
key: idx_email→ idx_emailインデックスを使用 -
rows: 1→ 1行だけスキャン(少ない=速い!)
インデックスを使っていない例❌
EXPLAIN SELECT * FROM users WHERE email LIKE '%example.com';
結果:
| type | key | rows | Extra |
|------|------|---------|-------------|
| ALL | NULL | 1000000 | Using where |
読み方:
-
type: ALL→ フルスキャン!遅い!😱 -
key: NULL→ インデックスを使っていない -
rows: 1000000→ 100万行全部スキャン!
PostgreSQL
EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'tanaka@example.com';
10. 実践:インデックスを設計しよう!🎯
10.1 シナリオ:ECサイトのordersテーブル
テーブル定義
CREATE TABLE orders (
order_id INT AUTO_INCREMENT PRIMARY KEY,
customer_id INT NOT NULL,
order_date DATETIME NOT NULL,
status VARCHAR(20),
total_amount DECIMAL(10,2),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
よく実行されるクエリ
-- クエリ1:顧客の注文履歴を取得
SELECT * FROM orders WHERE customer_id = 1 ORDER BY order_date DESC;
-- クエリ2:特定期間の注文を取得
SELECT * FROM orders
WHERE order_date BETWEEN '2026-01-01' AND '2026-01-31';
-- クエリ3:ステータスが'pending'の注文を取得
SELECT * FROM orders WHERE status = 'pending';
-- クエリ4:顧客ごとの注文金額の合計
SELECT customer_id, SUM(total_amount)
FROM orders
GROUP BY customer_id;
最適なインデックス設計🎨
-- インデックス1:顧客IDと注文日(複合)
-- クエリ1と4を高速化
CREATE INDEX idx_customer_date ON orders(customer_id, order_date DESC);
-- インデックス2:注文日
-- クエリ2を高速化
CREATE INDEX idx_order_date ON orders(order_date);
-- インデックス3:ステータス
-- クエリ3を高速化(ただし、選択性が低いので要検討)
CREATE INDEX idx_status ON orders(status);
なぜこの設計?🤔
インデックス1:(customer_id, order_date DESC)
-- このクエリに最適!
SELECT * FROM orders WHERE customer_id = 1 ORDER BY order_date DESC;
-- customer_idで絞り込む → order_date順に並んでいる!
-- ソート不要!超高速!⚡⚡
インデックス2:(order_date)
-- 日付範囲での検索に必要
SELECT * FROM orders
WHERE order_date BETWEEN '2026-01-01' AND '2026-01-31';
インデックス3:(status) は要検討⚠️
statusの値:pending, shipped, delivered, cancelled, returned
→ 5種類しかない(選択性が低い)
もし'pending'が全体の40%なら、
フルスキャンの方が速いかも...🤔
実際のデータ分布を見て判断!
11. インデックス設計のベストプラクティス💡
11.1 ✅ インデックスを作るべきカラム
- 主キー(自動的にインデックスが作られる)
- 外部キー(JOINで使われる)
- WHERE句でよく使われるカラム
- ORDER BY句でよく使われるカラム
- GROUP BY句でよく使われるカラム
11.2 ❌ インデックスを作らない方が良いカラム
- 選択性が低いカラム(性別、ブール値など)
- 更新が非常に頻繁なカラム
- 小さいテーブル(数百行程度)
- ほとんど検索されないカラム
11.3 複合インデックスの設計指針📏
1. 等価検索(=) → 範囲検索(>,<,BETWEEN) の順
2. 選択性が高い → 低い の順
3. よく単独で検索される → されない の順
例
-- 良い例✅
CREATE INDEX idx_good ON orders(customer_id, status, order_date);
-- 等価 等価 範囲
-- このクエリに最適
SELECT * FROM orders
WHERE customer_id = 1
AND status = 'shipped'
AND order_date >= '2026-01-01';
11.4 インデックスの数は最小限に!⚖️
「とりあえず全部にインデックス!」は NG!❌
理由:
- ストレージを圧迫💾
- 更新が遅くなる🐢
- 保守コストが増える🔧
必要最小限にする!
目安
小〜中規模テーブル:2〜5個
大規模テーブル:5〜10個
それ以上必要な場合は、テーブル設計を見直す!
12. インデックスの削除🗑️
12.1 不要なインデックスは削除する
-- インデックスの削除
DROP INDEX idx_email ON users;
-- または(MySQL)
ALTER TABLE users DROP INDEX idx_email;
12.2 不要なインデックスの見つけ方🔍
MySQL
-- 使われていないインデックスを確認(MySQL 5.7+)
SELECT *
FROM sys.schema_unused_indexes
WHERE object_schema = 'your_database';
PostgreSQL
-- インデックスのサイズと使用状況
SELECT
schemaname,
tablename,
indexname,
pg_size_pretty(pg_relation_size(indexrelid)) AS index_size,
idx_scan AS index_scans
FROM pg_stat_user_indexes
ORDER BY idx_scan ASC, pg_relation_size(indexrelid) DESC;
-- idx_scan が 0 または非常に少ない = 使われていない!
13. 実践演習:インデックスを最適化しよう!💪
13.1 問題
以下のテーブルとクエリがあります。
最適なインデックスを設計してください!
テーブル定義
CREATE TABLE products (
product_id INT AUTO_INCREMENT PRIMARY KEY,
product_name VARCHAR(100) NOT NULL,
category VARCHAR(50),
price DECIMAL(10,2),
stock_quantity INT,
is_available BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- データ:100万件
よく実行されるクエリ
-- クエリ1(最頻出):カテゴリで絞り込み、価格順に表示
SELECT * FROM products
WHERE category = 'electronics'
ORDER BY price ASC
LIMIT 20;
-- クエリ2:価格範囲で検索
SELECT * FROM products
WHERE price BETWEEN 1000 AND 5000;
-- クエリ3:在庫ありの商品を取得
SELECT * FROM products
WHERE is_available = TRUE
AND stock_quantity > 0;
-- クエリ4:商品名で検索
SELECT * FROM products
WHERE product_name LIKE 'iPhone%';
🤔 考えてみよう!
- どのインデックスを作成すべき?
- 複合インデックスは必要?
- カラムの順序は?
解答例を見る👇
✅ 推奨インデックス設計
-- インデックス1:クエリ1を最適化
CREATE INDEX idx_category_price ON products(category, price);
-- 理由:
-- - categoryで絞り込み(等価検索)
-- - priceで並び替え
-- - この順序で複合インデックスを作ると、ソート不要!
-- インデックス2:クエリ2を最適化
CREATE INDEX idx_price ON products(price);
-- 理由:
-- - 価格範囲検索で使用
-- - 単独でpriceだけの検索もあるため
-- インデックス3:クエリ4を最適化
CREATE INDEX idx_product_name ON products(product_name);
-- 理由:
-- - 前方一致検索で使用
-- - LIKE 'iPhone%' はインデックスが効く
-- インデックス4:クエリ3は要検討⚠️
-- is_available は BOOLEAN(選択性が低い)
-- stock_quantity はどうか?
-- もしstock_quantity > 0 が全体の10%以下なら:
CREATE INDEX idx_stock ON products(stock_quantity);
-- もし50%以上なら、インデックスは不要(フルスキャンの方が速い)
📊 検証:EXPLAINで確認
-- クエリ1の実行計画
EXPLAIN SELECT * FROM products
WHERE category = 'electronics'
ORDER BY price ASC
LIMIT 20;
-- 期待する結果:
-- type: ref (インデックス使用✅)
-- key: idx_category_price
-- Extra: Using index (カバリングインデックスの場合)
🎯 さらなる最適化
クエリ1をさらに高速化したいなら、カバリングインデックス:
CREATE INDEX idx_category_price_covering
ON products(category, price, product_id, product_name, stock_quantity);
-- これで、テーブルアクセスなしで結果を返せる!⚡⚡
-- ただし、インデックスサイズは大きくなる
14. まとめ🎓
お疲れ様でした!🎉
インデックスについて、しっかり学べましたね!
14.1 今回学んだこと📚
インデックスとは
- データベースの「索引」「目次」✨
- 検索を劇的に高速化する仕組み🚀
- B-Tree構造でデータを管理🌳
インデックスの効果
- フルスキャン → インデックススキャン
- 100万件でも0.01秒で検索!⚡
- ただしデメリットもある⚖️
インデックスの種類
- 単一カラムインデックス - 1つのカラムに作成
- 複合インデックス - 複数カラムの組み合わせ
- ユニークインデックス - 重複を許さない
- カバリングインデックス - クエリに必要な全カラムを含む
設計のポイント💡
- ✅ WHERE句でよく使うカラムに作成
- ✅ 外部キーには必須
- ✅ 複合インデックスは順序が重要
- ❌ むやみに増やさない
- ❌ 選択性が低いカラムは避ける
覚えておくこと
読み取り重視 → インデックスを積極的に
更新重視 → インデックスは最小限に
14.2 実務での使い方🏢
1. テーブル設計時:主キー、外部キーにインデックス
↓
2. 運用開始:スロークエリログで遅いクエリを特定
↓
3. EXPLAINで実行計画を確認
↓
4. 必要なインデックスを追加
↓
5. 効果測定:速くなったか確認
↓
6. 定期的に不要なインデックスを削除
14.3 次回予告🚀
次回はトランザクションとACID特性について学びます!
「複数の処理をまとめて実行したい」
「途中でエラーが出たら、全部なかったことにしたい」
「同時に複数人がアクセスしても、データが壊れないようにしたい」
こんな悩みを解決するトランザクションの仕組みを、
初心者向けにわかりやすく解説します!
BEGIN; -- トランザクション開始
-- 複数の処理
UPDATE accounts SET balance = balance - 1000 WHERE account_id = 1;
UPDATE accounts SET balance = balance + 1000 WHERE account_id = 2;
COMMIT; -- 成功!両方とも実行される✅
-- もしエラーが出たら...
ROLLBACK; -- 全部なかったことに!🔄
お楽しみに!👋
データベース設計基礎シリーズ
- データベース設計の基本概念(ER図、エンティティ)
- SQLの基本をマスターしよう
- 正規化入門(第1〜第3正規形)
- インデックスの仕組みと使い方 ← 今回✅
- トランザクションとACID特性 ← 次回
- 実践的なテーブル設計パターン集
- よくあるアンチパターンと対策
💬 質問や感想があれば、コメント欄でお気軽にどうぞ!
👍 役に立ったら、いいね&ストックをお願いします!
それでは、また次回!🙌