メインコンテンツにスキップ

⚖️ SQL vs NoSQL

📖 定義

SQL(リレーショナルデータベース)はテーブル形式で構造化されたデータを格納し、SQL言語で操作します。NoSQL(非リレーショナルデータベース)は柔軟なスキーマで様々な形式のデータを格納し、水平拡張に優れています。それぞれ長所と短所があり、プロジェクトの要件に応じて選択する必要があります。

🎯 例えで理解する

図書館 vs 倉庫

SQL (リレーショナルDB) = 整理された図書館
├─ すべての本が決められた場所に分類
├─ 図書カードに正確な情報を記録
├─ 厳格な貸出・返却ルール
└─ 見つけやすいが柔軟性が低い

NoSQL (非リレーショナルDB) = 柔軟な倉庫
├─ 様々な形状の物を保管可能
├─ 素早く追加・削除可能
├─ スペース拡張が簡単
└─ 柔軟だが整理が難しい場合がある

学校の出席簿 vs ソーシャルメディア

SQL = 学校の出席簿
学生テーブル:
┌────┬─────────┬─────┬────────┐
│ ID │ 名前 │年齢 │ クラス │
├────┼─────────┼─────┼────────┤
│ 1 │ 田中太郎│ 15 │ 1組 │
│ 2 │ 鈴木花子│ 15 │ 2組 │
└────┴─────────┴─────┴────────┘
- すべての学生が同じ情報項目を持つ

NoSQL = ソーシャルメディアプロフィール
ユーザー1: { name: "田中太郎", age: 15, hobbies: ["読書", "ゲーム"] }
ユーザー2: { name: "鈴木花子", city: "東京", job: "学生", pets: 2 }
- 各ユーザーが異なる情報を持つことができる

⚙️ 動作原理

1. データ構造の比較

-- SQL: 構造化されたテーブル
users テーブル
┌────┬─────────┬──────────────────┬─────┐
│ id │ name │ email │ age │
├────┼─────────┼──────────────────┼─────┤
1 │ 田中太郎│ tanaka@mail.com25
2 │ 鈴木花子│ suzuki@mail.com30
└────┴─────────┴──────────────────┴─────┘

orders テーブル
┌────┬─────────┬─────────────┬───────┐
│ id │ user_id │ product │ price │
├────┼─────────┼─────────────┼───────┤
11 │ ノートPC │ 1500
21 │ マウス │ 30
└────┴─────────┴─────────────┴───────┘
// NoSQL: 柔軟なドキュメント
// MongoDB 例
{
_id: ObjectId("507f1f77bcf86cd799439011"),
name: "田中太郎",
email: "tanaka@mail.com",
age: 25,
orders: [ // ネストされたドキュメント
{
product: "ノートPC",
price: 1500,
date: "2024-01-15"
},
{
product: "マウス",
price: 30,
date: "2024-01-20"
}
],
preferences: { // 様々な構造
theme: "dark",
notifications: true
}
}

2. NoSQLデータベースのタイプ

1. Document DB (ドキュメント型)
└─ MongoDB, CouchDB
└─ JSON形式のドキュメントを格納

2. Key-Value DB (キー値型)
└─ Redis, DynamoDB
└─ 高速キャッシング、セッション保存

3. Column-Family DB (カラム型)
└─ Cassandra, HBase
└─ 大規模データ分析

4. Graph DB (グラフ型)
└─ Neo4j, Amazon Neptune
└─ 関係中心のデータ (ソーシャルネットワーク)

💡 実例

SQL 例 (MySQL)

-- テーブル作成 (厳格なスキーマ)
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
age INT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE orders (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT,
product VARCHAR(100),
price DECIMAL(10, 2),
FOREIGN KEY (user_id) REFERENCES users(id)
);

-- データ挿入
INSERT INTO users (name, email, age)
VALUES ('田中太郎', 'tanaka@mail.com', 25);

-- JOINで関係を取得
SELECT
users.name,
orders.product,
orders.price
FROM users
INNER JOIN orders ON users.id = orders.user_id
WHERE users.name = '田中太郎';

-- トランザクション (ACID保証)
BEGIN TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;

NoSQL 例 (MongoDB)

// スキーマなし - 自由な構造
db.users.insertOne({
name: "田中太郎",
email: "tanaka@mail.com",
age: 25,
orders: [
{ product: "ノートPC", price: 1500 },
{ product: "マウス", price: 30 }
]
});

// 異なる構造も可能!
db.users.insertOne({
name: "鈴木花子",
email: "suzuki@mail.com",
hobbies: ["読書", "旅行"], // 異なるフィールド
address: { // ネストされたオブジェクト
city: "東京",
zipcode: "12345"
}
});

// 検索
db.users.find({ name: "田中太郎" });

// ネストされたドキュメント検索
db.users.find({
"orders.product": "ノートPC"
});

// 更新
db.users.updateOne(
{ name: "田中太郎" },
{
$set: { age: 26 },
$push: {
orders: { product: "キーボード", price: 80 }
}
}
);

// 集計 (Aggregation)
db.users.aggregate([
{ $unwind: "$orders" },
{ $group: {
_id: "$name",
totalSpent: { $sum: "$orders.price" }
}
}
]);

Node.jsでのSQL vs NoSQL

// ============ SQL (MySQL) ============
const mysql = require('mysql2/promise');

const pool = mysql.createPool({
host: 'localhost',
user: 'root',
password: 'password',
database: 'myapp'
});

// ユーザーと注文を取得 (JOIN必要)
async function getUserWithOrders(userId) {
const [rows] = await pool.execute(`
SELECT
users.name,
users.email,
orders.product,
orders.price
FROM users
LEFT JOIN orders ON users.id = orders.user_id
WHERE users.id = ?
`, [userId]);

return rows;
}

// ============ NoSQL (MongoDB) ============
const { MongoClient } = require('mongodb');

const client = new MongoClient('mongodb://localhost:27017');
const db = client.db('myapp');
const users = db.collection('users');

// ユーザーと注文を取得 (JOIN不要 - すでに含まれている)
async function getUserWithOrders(userId) {
const user = await users.findOne({ _id: userId });
// user.orders にすでに注文情報が含まれている!
return user;
}

実践例: ブログシステム

// ============ SQL 方式 ============
// 正規化された構造
/*
posts テーブル:
id, title, content, author_id, created_at

comments テーブル:
id, post_id, user_id, content, created_at

tags テーブル:
id, name

post_tags テーブル:
post_id, tag_id
*/

// 投稿 + コメント + タグ取得 → 複数のJOIN必要
SELECT
posts.*,
comments.content as comment,
tags.name as tag
FROM posts
LEFT JOIN comments ON posts.id = comments.post_id
LEFT JOIN post_tags ON posts.id = post_tags.post_id
LEFT JOIN tags ON post_tags.tag_id = tags.id
WHERE posts.id = 1;

// ============ NoSQL 方式 ============
// 非正規化された構造
{
_id: ObjectId("..."),
title: "MongoDB紹介",
content: "MongoDBは...",
author: {
id: "user123",
name: "田中太郎"
},
comments: [
{
user: { id: "user456", name: "鈴木花子" },
content: "良い記事ですね!",
createdAt: "2024-01-15"
}
],
tags: ["データベース", "NoSQL"],
createdAt: "2024-01-10"
}

// 1回のクエリですべてのデータ取得!
db.posts.findOne({ _id: ObjectId("...") });

🤔 よくある質問

Q1. どちらを選ぶべきですか?

A: プロジェクトの特性に応じて選択してください:

✅ SQLを選ぶ場合:
├─ データ構造が明確で変わらない
├─ 複雑な関係とJOINが多い
├─ ACIDトランザクションが必須 (金融、Eコマース)
├─ データ整合性が重要
└─ 例: 銀行システム、会計プログラム、ERP

✅ NoSQLを選ぶ場合:
├─ データ構造が流動的
├─ 高速な読み書きが重要
├─ 水平拡張(スケールアウト)が必要
├─ 大容量データ処理
└─ 例: ソーシャルメディア、IoT、リアルタイム分析、ログ

Q2. 主な違いは?

A:

特性SQLNoSQL
データ構造テーブル (行、列)ドキュメント、キー値、グラフなど
スキーマ固定 (厳格)柔軟 (動的)
拡張垂直拡張 (Scale Up)水平拡張 (Scale Out)
関係JOINで接続ネストまたは参照
トランザクションACID保証最終的整合性 (BASE)
クエリSQL言語各DBごとに異なる
MySQL, PostgreSQLMongoDB, Redis
// SQL: ACID
Atomicity (原子性): 全部または全無
Consistency (一貫性): ルール違反不可
Isolation (独立性): トランザクション間独立
Durability (永続性): 永久保存

// NoSQL: BASE
Basically Available (基本的可用性)
Soft state (柔軟な状態)
Eventually consistent (最終的整合性)

Q3. パフォーマンスの違いは?

A: 使用パターンによって異なります:

// SQL 利点: 複雑なクエリ、JOIN
// 複数のテーブルからデータを結合
SELECT u.name, p.title, c.content
FROM users u
JOIN posts p ON u.id = p.author_id
JOIN comments c ON p.id = c.post_id
WHERE u.age > 20
ORDER BY p.created_at DESC;
// → 強力で正確だが遅い場合がある

// NoSQL 利点: 高速な読み書き
// 単一ドキュメントにすべての情報が含まれる
db.posts.find({ author_age: { $gt: 20 } })
.sort({ created_at: -1 });
// → 非常に高速だが複雑なJOINは困難

// ベンチマーク例
SQL: 10,000 reads/sec (複雑なJOIN)
NoSQL: 100,000 reads/sec (単純検索)

SQL: 5,000 writes/sec (トランザクション保証)
NoSQL: 50,000 writes/sec (高速書き込み)

Q4. 一緒に使えますか?

A: はい! ポリグロットパーシステンス(Polyglot Persistence)と言います:

// 例: Eコマースシステム

// 1. SQL (MySQL) - 注文、決済
// → ACIDトランザクション必須
{
orders: "MySQL",
payments: "PostgreSQL",
inventory: "MySQL"
}

// 2. NoSQL (MongoDB) - 商品カタログ
// → 柔軟な属性、高速検索
{
products: "MongoDB",
reviews: "MongoDB"
}

// 3. NoSQL (Redis) - キャッシング、セッション
// → 超高速読み書き
{
cache: "Redis",
sessions: "Redis",
realtime: "Redis"
}

// 4. Graph DB (Neo4j) - 推薦システム
// → 関係ベースのクエリ
{
recommendations: "Neo4j",
socialGraph: "Neo4j"
}

Q5. マイグレーションは難しいですか?

A: 戦略的にアプローチすれば可能です:

// SQL → NoSQL マイグレーション戦略

// 1. ハイブリッドアプローチ
// - 新機能はNoSQL
// - 既存機能はSQL維持

// 2. 段階的マイグレーション
// Step 1: 読み取り専用データから
// Step 2: 重要度の低い機能
// Step 3: コア機能

// 3. データ同期
// - 変更データキャプチャ (CDC)
// - イベントストリーミング (Kafka)

// 例: デュアル書き込み
async function createUser(userData) {
// SQLに書き込み
const sqlUser = await mysqlDB.insert(userData);

// NoSQLにも書き込み (非同期)
await mongooDB.insertOne({
...userData,
_id: sqlUser.id
}).catch(err => {
// 失敗してもSQLは維持
console.error('MongoDB sync failed:', err);
});

return sqlUser;
}

🎓 次のステップ

SQLとNoSQLを理解したら、次を学習してみましょう:

  1. データベースとは? - 基本概念
  2. Node.jsとは? - バックエンド開発
  3. Dockerとは? (ドキュメント作成予定) - データベースコンテナ化

実践してみる

# ============ MySQL 実践 ============
# インストール
brew install mysql # macOS

# 実行
mysql -u root -p

# テーブル作成とクエリ
CREATE DATABASE testdb;
USE testdb;
CREATE TABLE users (id INT, name VARCHAR(50));
INSERT INTO users VALUES (1, '田中太郎');
SELECT * FROM users;

# ============ MongoDB 実践 ============
# インストール
brew tap mongodb/brew
brew install mongodb-community

# 実行
mongod --config /usr/local/etc/mongod.conf

# MongoDB Shell
mongosh

# データ挿入と検索
use testdb
db.users.insertOne({ name: "田中太郎", age: 25 })
db.users.find()

🎬 まとめ

SQLとNoSQLはそれぞれ長所と短所があります:

  • SQL: 構造化データ、ACIDトランザクション、複雑な関係
  • NoSQL: 柔軟なスキーマ、高速パフォーマンス、水平拡張
  • 選択基準: データ構造、拡張性、整合性要件
  • ポリグロット: 必要に応じて複数のDBを組み合わせて使用

「銀の弾丸はない」- プロジェクトに合ったデータベースを選びましょう! ⚖️✨