跳至正文

💾 什麼是資料庫?

📖 定義

資料庫(Database, DB)是系統地結構化的資料集合。DBMS(Database Management System,資料庫管理系統)是建立、管理和存取資料庫的軟體。資料庫可以高效地儲存和檢索資料,允許多個使用者同時安全存取。

🎯 用類比理解

圖書館系統

將資料庫比作圖書館:

  • 資料庫: 整個圖書館
  • : 各個區域(小說、科學、歷史等)
  • 行(Row): 單獨的書
  • 列(Column): 書的屬性(標題、作者、出版年等)
  • 主鍵(Primary Key): 圖書唯一編號
  • DBMS: 圖書管理員和管理系統
圖書館 (資料庫)
├─ 小說區 (表)
│ ├─ 書1: 哈利波特, J.K.羅琳, 1997
│ ├─ 書2: 魔戒, 托爾金, 1954
│ └─ 書3: 小王子, 聖修伯里, 1943
├─ 科學區 (表)
│ ├─ 書1: 宇宙, 卡爾·薩根, 1980
│ └─ 書2: 自私的基因, 理查·道金斯, 1976
└─ 借閱記錄 (表)
├─ 記錄1: 會員123, 哈利波特, 2024-01-15
└─ 記錄2: 會員456, 宇宙, 2024-01-20

⚙️ 工作原理

1. 資料庫結構

資料庫 (Database)
└─ 表 (Table)
├─ 行 (Row/Record) - 單個資料項
│ └─ 例: 一個使用者的資訊
└─ 列 (Column/Field) - 資料屬性
└─ 例: 姓名、郵箱、年齡

2. 關聯式資料庫範例

-- users表
users
┌────┬────────┬──────────────────┬─────┐
│ id │ name │ email │ age │
├────┼────────┼──────────────────┼─────┤
1 │ 王小明 │ wang@mail.com25
2 │ 李小華 │ lee@mail.com30
3 │ 張大衛 │ chang@mail.com28
└────┴────────┴──────────────────┴─────┘

-- orders表
orders
┌────┬─────────┬─────────────┬───────┐
│ id │ user_id │ product │ price │
├────┼─────────┼─────────────┼───────┤
11 │ 筆記型電腦 │ 1500
21 │ 滑鼠 │ 30
32 │ 鍵盤 │ 80
└────┴─────────┴─────────────┴───────┘

└─ 外鍵: 引用users表的id

3. CRUD操作

CRUD是資料庫的4種基本操作:

C - Create (建立): 新增資料
R - Read (讀取): 查詢資料
U - Update (更新): 修改現有資料
D - Delete (刪除): 刪除資料

💡 實際範例

使用SQL進行CRUD

-- CREATE (建立): 建立表
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 (建立): 新增資料
INSERT INTO users (name, email, age)
VALUES ('王小明', 'wang@mail.com', 25);

INSERT INTO users (name, email, age)
VALUES
('李小華', 'lee@mail.com', 30),
('張大衛', 'chang@mail.com', 28);

-- READ (讀取): 查詢所有使用者
SELECT * FROM users;

-- READ (讀取): 條件查詢
SELECT name, email FROM users
WHERE age >= 28;

-- READ (讀取): 排序查詢
SELECT * FROM users
ORDER BY age DESC;

-- READ (讀取): 搜尋
SELECT * FROM users
WHERE name LIKE '王%';

-- UPDATE (更新): 修改資料
UPDATE users
SET age = 26
WHERE name = '王小明';

-- UPDATE (更新): 修改多個欄位
UPDATE users
SET email = 'new@mail.com', age = 31
WHERE id = 2;

-- DELETE (刪除): 刪除資料
DELETE FROM users
WHERE id = 3;

-- DELETE (刪除): 條件刪除
DELETE FROM users
WHERE age < 20;

Node.js中使用資料庫

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

// 資料庫連接
const pool = mysql.createPool({
host: 'localhost',
user: 'root',
password: 'password',
database: 'myapp',
waitForConnections: true,
connectionLimit: 10
});

// CREATE - 新增使用者
async function createUser(name, email, age) {
const [result] = await pool.execute(
'INSERT INTO users (name, email, age) VALUES (?, ?, ?)',
[name, email, age]
);
console.log('建立的ID:', result.insertId);
return result.insertId;
}

// READ - 取得所有使用者
async function getAllUsers() {
const [rows] = await pool.execute('SELECT * FROM users');
return rows;
}

// READ - 取得特定使用者
async function getUserById(id) {
const [rows] = await pool.execute(
'SELECT * FROM users WHERE id = ?',
[id]
);
return rows[0];
}

// UPDATE - 更新使用者資訊
async function updateUser(id, name, email, age) {
const [result] = await pool.execute(
'UPDATE users SET name = ?, email = ?, age = ? WHERE id = ?',
[name, email, age, id]
);
console.log('更新的行數:', result.affectedRows);
return result.affectedRows;
}

// DELETE - 刪除使用者
async function deleteUser(id) {
const [result] = await pool.execute(
'DELETE FROM users WHERE id = ?',
[id]
);
console.log('刪除的行數:', result.affectedRows);
return result.affectedRows;
}

// 使用範例
async function main() {
try {
// 建立
const userId = await createUser('王小明', 'wang@mail.com', 25);

// 查詢
const users = await getAllUsers();
console.log('所有使用者:', users);

// 查詢特定使用者
const user = await getUserById(userId);
console.log('特定使用者:', user);

// 更新
await updateUser(userId, '王小明', 'new@mail.com', 26);

// 刪除
await deleteUser(userId);
} catch (error) {
console.error('錯誤:', error);
}
}

main();

ORM使用 (Sequelize)

const { Sequelize, DataTypes } = require('sequelize');

// 資料庫連接
const sequelize = new Sequelize('myapp', 'root', 'password', {
host: 'localhost',
dialect: 'mysql'
});

// 模型定義 (表結構)
const User = sequelize.define('User', {
name: {
type: DataTypes.STRING(50),
allowNull: false
},
email: {
type: DataTypes.STRING(100),
allowNull: false,
unique: true
},
age: {
type: DataTypes.INTEGER
}
});

// CRUD操作 (無需SQL,只用JavaScript!)
async function ormExample() {
// CREATE - 建立
const user = await User.create({
name: '王小明',
email: 'wang@mail.com',
age: 25
});
console.log('建立:', user.toJSON());

// READ - 查詢
const allUsers = await User.findAll();
console.log('所有使用者:', allUsers);

// READ - 條件查詢
const youngUsers = await User.findAll({
where: { age: { [Sequelize.Op.gte]: 20 } },
order: [['age', 'DESC']]
});

// READ - 查詢單個
const oneUser = await User.findOne({
where: { email: 'wang@mail.com' }
});

// UPDATE - 更新
await User.update(
{ age: 26 },
{ where: { name: '王小明' } }
);

// DELETE - 刪除
await User.destroy({
where: { id: 1 }
});
}

關係設定 (JOIN)

// 使用者和訂單的關係設定
const User = sequelize.define('User', {
name: DataTypes.STRING,
email: DataTypes.STRING
});

const Order = sequelize.define('Order', {
product: DataTypes.STRING,
price: DataTypes.DECIMAL(10, 2)
});

// 關係定義: 一個使用者可以有多個訂單
User.hasMany(Order);
Order.belongsTo(User);

// JOIN查詢
async function getUserWithOrders(userId) {
const userWithOrders = await User.findOne({
where: { id: userId },
include: Order // JOIN
});

console.log(userWithOrders.toJSON());
// {
// id: 1,
// name: '王小明',
// email: 'wang@mail.com',
// Orders: [
// { product: '筆記型電腦', price: 1500 },
// { product: '滑鼠', price: 30 }
// ]
// }
}

🤔 常見問題

Q1. 為什麼使用資料庫?

A: 比檔案系統有很多優勢:

❌ 檔案系統 (例: users.txt)
├─ 可能資料重複
├─ 並行存取問題
├─ 備份/恢復困難
├─ 安全漏洞
└─ 搜尋慢

✅ 資料庫
├─ 保證資料完整性
├─ 並行控制 (多使用者)
├─ 自動備份/恢復
├─ 存取權限管理
├─ 快速搜尋 (索引)
└─ 交易支援

Q2. 應該選擇哪個資料庫?

A: 取決於專案需求:

// 關聯式資料庫 (SQL)
MySQL / PostgreSQL / SQLite
├─ 優點: 精確的資料結構、交易
├─ 缺點: 靈活性低、可擴展性有限
└─ 適用: 金融、電子商務、使用者管理

// 非關聯式資料庫 (NoSQL)
MongoDB / Redis / Cassandra
├─ 優點: 靈活的模式、水平擴展
├─ 缺點: 複雜關係處理困難
└─ 適用: 社群媒體、即時分析、快取

Q3. 什麼是索引(Index)?

A: 加速資料檢索的資料結構:

-- 沒有索引
SELECT * FROM users WHERE email = 'test@mail.com';
-- → 檢查所有100萬行 (慢)

-- 建立索引
CREATE INDEX idx_email ON users(email);

-- 有索引
SELECT * FROM users WHERE email = 'test@mail.com';
-- → 透過索引直接查找 (快!)

-- 缺點: 資料插入/更新時索引也要更新 (寫入變慢)

類比: 就像書的目錄或索引,可以快速找到想要的內容。

Q4. 什麼是交易(Transaction)?

A: 將多個操作作為一個單元捆綁處理:

// 銀行轉帳範例
async function transferMoney(fromId, toId, amount) {
const connection = await pool.getConnection();

try {
// 開始交易
await connection.beginTransaction();

// 1. 減少發送方餘額
await connection.execute(
'UPDATE accounts SET balance = balance - ? WHERE id = ?',
[amount, fromId]
);

// 2. 增加接收方餘額
await connection.execute(
'UPDATE accounts SET balance = balance + ? WHERE id = ?',
[amount, toId]
);

// 全部成功 → 提交
await connection.commit();
console.log('轉帳完成!');

} catch (error) {
// 發生錯誤 → 全部取消
await connection.rollback();
console.log('轉帳失敗,已回滾');
throw error;
} finally {
connection.release();
}
}

// ACID原則
// Atomicity (原子性): 全部執行 or 全部取消
// Consistency (一致性): 不違反規則
// Isolation (隔離性): 與其他交易獨立
// Durability (持久性): 完成後永久儲存

Q5. 什麼是SQL注入(SQL Injection)?

A: 注入惡意SQL程式碼的攻擊:

// ❌ 危險程式碼 (SQL注入漏洞)
const email = req.body.email; // "admin@mail.com' OR '1'='1"
const query = `SELECT * FROM users WHERE email = '${email}'`;
// 執行: SELECT * FROM users WHERE email = 'admin@mail.com' OR '1'='1'
// → 所有使用者資訊洩露!

// ✅ 安全程式碼 (預處理語句)
const [rows] = await pool.execute(
'SELECT * FROM users WHERE email = ?',
[email] // 自動轉義
);

// ORM使用 (自動防護)
const user = await User.findOne({
where: { email: email } // 安全
});

🎓 下一步

了解資料庫後,繼續學習:

  1. SQL vs NoSQL - 資料庫選擇指南
  2. 什麼是Node.js? - 後端開發
  3. 什麼是API? - 資料庫與前端連接

實作練習

# MySQL 安裝和執行
brew install mysql # macOS
sudo apt install mysql # Linux

# 連接資料庫
mysql -u root -p

# 建立資料庫
CREATE DATABASE myapp;
USE myapp;

# 建立表和新增資料
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50),
email VARCHAR(100)
);

INSERT INTO users (name, email) VALUES ('王小明', 'test@mail.com');
SELECT * FROM users;

🎬 總結

資料庫是所有應用程式的核心:

  • 資料庫: 結構化資料集合
  • CRUD: 建立、讀取、更新、刪除 - 基本操作
  • : 以行和列組織資料
  • 關係: 表之間的連接 (JOIN)
  • 交易: 安全的資料處理

深入理解資料庫能夠構建穩定且可擴展的應用程式! 💾✨