Technology

database design คือ

database design คอ
database design คือ | SiamCafe Blog
2026-03-14· อ. บอม — SiamCafe.net· 8,277 คำ

Database Design คือ

Database Design ออกแบบโครงสร้างฐานข้อมูล ลด Redundancy รักษา Integrity Scalability Conceptual Logical Physical Design ER Diagram Normalization Index

ขั้นตอนOutputเครื่องมือ
RequirementsData Requirements DocumentInterview, User Stories
ConceptualER DiagramERDPlus, Draw.io, Lucidchart
LogicalTables, Columns, RelationsMySQL Workbench, pgModeler
PhysicalIndexes, Partitions, StorageSQL DDL, Migration Tools
ImplementationDatabase SchemaSQL, ORM (SQLAlchemy, Prisma)

SQL Schema Design

# === Database Schema Design ===

# E-commerce Database Schema
# CREATE DATABASE ecommerce;
# USE ecommerce;

# -- Users Table
# CREATE TABLE users (
#     id INT PRIMARY KEY AUTO_INCREMENT,
#     email VARCHAR(255) UNIQUE NOT NULL,
#     password_hash VARCHAR(255) NOT NULL,
#     first_name VARCHAR(100) NOT NULL,
#     last_name VARCHAR(100) NOT NULL,
#     phone VARCHAR(20),
#     created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
#     updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
#     INDEX idx_email (email),
#     INDEX idx_name (first_name, last_name)
# );

# -- Categories Table
# CREATE TABLE categories (
#     id INT PRIMARY KEY AUTO_INCREMENT,
#     name VARCHAR(100) NOT NULL,
#     slug VARCHAR(100) UNIQUE NOT NULL,
#     parent_id INT NULL,
#     description TEXT,
#     FOREIGN KEY (parent_id) REFERENCES categories(id)
# );

# -- Products Table
# CREATE TABLE products (
#     id INT PRIMARY KEY AUTO_INCREMENT,
#     name VARCHAR(255) NOT NULL,
#     slug VARCHAR(255) UNIQUE NOT NULL,
#     description TEXT,
#     price DECIMAL(10,2) NOT NULL,
#     stock INT DEFAULT 0,
#     category_id INT NOT NULL,
#     is_active BOOLEAN DEFAULT TRUE,
#     created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
#     FOREIGN KEY (category_id) REFERENCES categories(id),
#     INDEX idx_category (category_id),
#     INDEX idx_price (price),
#     INDEX idx_active_category (is_active, category_id)
# );

# -- Orders Table
# CREATE TABLE orders (
#     id INT PRIMARY KEY AUTO_INCREMENT,
#     user_id INT NOT NULL,
#     status ENUM('pending','paid','shipped','delivered','cancelled') DEFAULT 'pending',
#     total_amount DECIMAL(10,2) NOT NULL,
#     shipping_address TEXT NOT NULL,
#     created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
#     FOREIGN KEY (user_id) REFERENCES users(id),
#     INDEX idx_user (user_id),
#     INDEX idx_status (status),
#     INDEX idx_created (created_at)
# );

# -- Order Items Table
# CREATE TABLE order_items (
#     id INT PRIMARY KEY AUTO_INCREMENT,
#     order_id INT NOT NULL,
#     product_id INT NOT NULL,
#     quantity INT NOT NULL,
#     unit_price DECIMAL(10,2) NOT NULL,
#     FOREIGN KEY (order_id) REFERENCES orders(id) ON DELETE CASCADE,
#     FOREIGN KEY (product_id) REFERENCES products(id),
#     INDEX idx_order (order_id)
# );

# Python Schema Builder
from dataclasses import dataclass, field
from typing import List, Optional
from enum import Enum

class DataType(Enum):
    INT = "INT"
    VARCHAR = "VARCHAR"
    TEXT = "TEXT"
    DECIMAL = "DECIMAL"
    BOOLEAN = "BOOLEAN"
    TIMESTAMP = "TIMESTAMP"
    ENUM = "ENUM"

@dataclass
class Column:
    name: str
    data_type: DataType
    length: Optional[int] = None
    nullable: bool = True
    primary_key: bool = False
    unique: bool = False
    default: Optional[str] = None
    foreign_key: Optional[str] = None

@dataclass
class Table:
    name: str
    columns: List[Column] = field(default_factory=list)
    indexes: List[str] = field(default_factory=list)

    def add_column(self, col: Column):
        self.columns.append(col)

    def generate_sql(self) -> str:
        lines = [f"CREATE TABLE {self.name} ("]
        col_defs = []
        for col in self.columns:
            d = f"  {col.name} {col.data_type.value}"
            if col.length:
                d += f"({col.length})"
            if col.primary_key:
                d += " PRIMARY KEY AUTO_INCREMENT"
            if not col.nullable and not col.primary_key:
                d += " NOT NULL"
            if col.unique:
                d += " UNIQUE"
            if col.default:
                d += f" DEFAULT {col.default}"
            col_defs.append(d)
        for col in self.columns:
            if col.foreign_key:
                col_defs.append(f"  FOREIGN KEY ({col.name}) REFERENCES {col.foreign_key}")
        for idx in self.indexes:
            col_defs.append(f"  INDEX idx_{idx} ({idx})")
        lines.append(",\n".join(col_defs))
        lines.append(");")
        return "\n".join(lines)

# สร้าง Table
users = Table("users")
users.add_column(Column("id", DataType.INT, primary_key=True))
users.add_column(Column("email", DataType.VARCHAR, 255, nullable=False, unique=True))
users.add_column(Column("name", DataType.VARCHAR, 100, nullable=False))
users.indexes = ["email"]

print("Generated SQL:")
print(users.generate_sql())

Normalization

# normalization.py — Database Normalization
normal_forms = {
    "1NF (First Normal Form)": {
        "rule": "ทุก Column เก็บค่าเดียว ไม่มี Repeating Groups",
        "bad": "name: 'John, Jane' | phones: '081,082'",
        "good": "แยกเป็น row ละคน row ละเบอร์",
        "fix": "แยก Multi-valued Columns เป็นตารางใหม่",
    },
    "2NF (Second Normal Form)": {
        "rule": "1NF + ไม่มี Partial Dependency (ทุก Column ขึ้นกับ PK ทั้งหมด)",
        "bad": "order_items(order_id, product_id, product_name) — product_name ขึ้นกับ product_id อย่างเดียว",
        "good": "แยก product_name ไปตาราง products",
        "fix": "แยก Column ที่ขึ้นกับ PK บางส่วนไปตารางใหม่",
    },
    "3NF (Third Normal Form)": {
        "rule": "2NF + ไม่มี Transitive Dependency (ไม่มี non-key ขึ้นกับ non-key)",
        "bad": "employees(id, dept_id, dept_name) — dept_name ขึ้นกับ dept_id ไม่ใช่ id",
        "good": "แยก dept_name ไปตาราง departments",
        "fix": "แยก Column ที่ขึ้นกับ non-key ไปตารางใหม่",
    },
    "BCNF (Boyce-Codd)": {
        "rule": "3NF + ทุก Determinant เป็น Candidate Key",
        "bad": "กรณีพิเศษที่มี Candidate Key หลายตัวซ้อนกัน",
        "good": "แยกตารางให้ทุก Determinant เป็น Key",
        "fix": "ไม่ค่อยพบในทางปฏิบัติ 3NF เพียงพอ",
    },
}

print("=== Database Normalization ===\n")
for nf, info in normal_forms.items():
    print(f"  [{nf}]")
    print(f"    Rule: {info['rule']}")
    print(f"    Bad:  {info['bad']}")
    print(f"    Good: {info['good']}")
    print(f"    Fix:  {info['fix']}\n")

# Denormalization
denorm_cases = {
    "Read-heavy": "JOIN หลายตารางช้า Cache ข้อมูลซ้ำในตาราง",
    "Reporting": "สร้าง Materialized View หรือ Summary Table",
    "Caching": "เก็บ Computed Values เช่น total_amount",
    "NoSQL": "Document DB เก็บ Nested Data ไม่ต้อง Normalize",
}

print("=== When to Denormalize ===")
for case, desc in denorm_cases.items():
    print(f"  {case}: {desc}")

Indexing Strategy

# indexing.py — Database Indexing Strategy
index_types = {
    "B-Tree Index": {
        "use": "ค้นหา =, <, >, BETWEEN, ORDER BY",
        "example": "CREATE INDEX idx_price ON products(price)",
        "when": "Column ที่ถูก WHERE, ORDER BY บ่อย",
    },
    "Hash Index": {
        "use": "ค้นหา = เท่านั้น (Exact Match)",
        "example": "CREATE INDEX idx_email ON users USING HASH(email)",
        "when": "Lookup by exact value เท่านั้น",
    },
    "Composite Index": {
        "use": "ค้นหาหลาย Column พร้อมกัน",
        "example": "CREATE INDEX idx_cat_price ON products(category_id, price)",
        "when": "Query ที่ WHERE หลาย Column ลำดับสำคัญ",
    },
    "Full-text Index": {
        "use": "ค้นหาข้อความ MATCH AGAINST",
        "example": "CREATE FULLTEXT INDEX idx_desc ON products(description)",
        "when": "Search ข้อความยาว Blog Content",
    },
    "Partial Index": {
        "use": "Index เฉพาะ rows ที่ตรงเงื่อนไข",
        "example": "CREATE INDEX idx_active ON products(price) WHERE is_active=true",
        "when": "ลดขนาด Index เฉพาะข้อมูลที่ใช้บ่อย",
    },
}

print("=== Index Types ===")
for idx_type, info in index_types.items():
    print(f"\n  [{idx_type}]")
    for k, v in info.items():
        print(f"    {k}: {v}")

# Indexing Best Practices
practices = [
    "Index Column ที่ถูก WHERE, JOIN, ORDER BY บ่อย",
    "Composite Index ลำดับ Column สำคัญ (Selectivity สูงก่อน)",
    "ไม่ Index Column ที่มีค่าซ้ำมาก (Low Cardinality) เช่น gender",
    "EXPLAIN ANALYZE ทุก Query ก่อนสร้าง Index",
    "ลบ Index ที่ไม่ได้ใช้ ตรวจด้วย pg_stat_user_indexes",
    "Covering Index ใส่ Column ที่ SELECT ด้วยลด Table Lookup",
    "Index ใช้พื้นที่ Disk + ทำ INSERT/UPDATE ช้าลง",
]

print(f"\n\n=== Indexing Best Practices ===")
for i, p in enumerate(practices, 1):
    print(f"  {i}. {p}")

เคล็ดลับ

Database Design คืออะไร

ออกแบบโครงสร้างฐานข้อมูล ลด Redundancy Integrity Scalability ER Diagram Normalization Tables Columns Indexes

Normalization คืออะไร

จัดระเบียบตาราง ลด Redundancy 1NF ไม่ Repeating 2NF ไม่ Partial Dependency 3NF ไม่ Transitive ออกแบบถึง 3NF พอ

Primary Key กับ Foreign Key ต่างกันอย่างไร

PK ระบุแถวไม่ซ้ำ ไม่ NULL ตารางละ 1 FK อ้างอิง PK ตารางอื่น สร้างความสัมพันธ์ ตารางมีได้หลาย FK

Index คืออะไร ใช้เมื่อไหร่

โครงสร้างข้อมูลค้นหาเร็ว WHERE JOIN ORDER BY ใช้พื้นที่เพิ่ม INSERT UPDATE ช้าลง EXPLAIN ANALYZE ก่อนสร้าง

สรุป

Database Design ออกแบบฐานข้อมูล ER Diagram Normalization 1NF 2NF 3NF Primary Key Foreign Key Index B-Tree Composite Full-text EXPLAIN ANALYZE snake_case Soft Delete Denormalization

📖 บทความที่เกี่ยวข้อง

OWASP ZAP Database Migrationอ่านบทความ → React Server Components Multi-tenant Designอ่านบทความ → Prometheus PromQL Event Driven Designอ่านบทความ → Healthchecks.io Domain Driven Design DDDอ่านบทความ → Webhook Design Pattern Compliance Automationอ่านบทความ →

📚 ดูบทความทั้งหมด →