ERD dan Normalisasi Database: Tutorial Lengkap untuk Desain Database yang Optimal

By | September 27, 2025

 

ERD dan Normalisasi Database: Tutorial Lengkap untuk Desain Database yang Optimal

Pernah nemuin database yang kayak gudang berantakan? Data duplikat di mana-mana, update satu record harus ubah sepuluh tempat, dan query-nya lambat banget? Itu tanda-tanda database butuh ERD dan normalisasi! Duo dynamic ini ibarat architect dan interior designer untuk database-mu.

ERD (Entity Relationship Diagram) adalah blueprint-nya – mendesain struktur dan hubungan. Normalisasi database adalah Feng Shui-nya – menata segala sesuatu pada tempatnya agar harmonis dan efisien. Artikel ini akan memandu kamu memahami kedua konsep fundamental ini dengan contoh nyata dan tutorial step-by-step. Siap untuk mentransform database-mu dari chaos menjadi elegant?

Memahami Konsep Dasar: ERD vs Normalisasi

Sebelum menyelam lebih dalam, mari pahami peran masing-masing:

ERD (Entity Relationship Diagram)

ERD adalah representasi visual dari struktur database yang menunjukkan entities, attributes, dan relationships.

  • Fungsi: Mendesain struktur database secara visual
  • Analog: Blueprint bangunan
  • Output: Diagram yang mudah dipahami

Normalisasi Database

Normalisasi adalah proses mengorganisasi data untuk mengurangi redundancy dan meningkatkan integrity.

  • Fungsi: Mengoptimalkan struktur database
  • Analog: Tata letak ruangan yang efisien
  • Output: Struktur tabel yang optimal

Langkah 1: Membuat ERD – Blueprint Database

Mari mulai dengan studi kasus sistem perpustakaan universitas.

Identifikasi Entities dan Attributes

ENTITIES YANG DIBUTUHKAN:
1. Mahasiswa (Student)
   - student_id (Primary Key)
   - nama_lengkap
   - jurusan
   - angkatan
   - email
   - telepon

2. Buku (Book)
   - book_id (Primary Key) 
   - judul_buku
   - pengarang
   - penerbit
   - tahun_terbit
   - kategori
   - stok

3. Peminjaman (Loan)
   - loan_id (Primary Key)
   - student_id (Foreign Key)
   - book_id (Foreign Key)
   - tanggal_pinjam
   - tanggal_kembali
   - status_peminjaman

4. Denda (Fine)
   - fine_id (Primary Key)
   - loan_id (Foreign Key)
   - jumlah_denda
   - status_bayar
   - tanggal_bayar

Tentukan Relationships antar Entities

Cardinality Relationships:

  • Mahasiswa → Peminjaman: One-to-Many (1:M) – Satu mahasiswa bisa banyak peminjaman
  • Buku → Peminjaman: One-to-Many (1:M) – Satu buku bisa dipinjam banyak kali
  • Peminjaman → Denda: One-to-One (1:1) – Satu peminjaman punya satu denda (jika telat)

Simbol-Simbol ERD yang Penting

Simbol Keterangan Contoh
□ Rectangle Entity Mahasiswa, Buku
○ Oval Attribute student_id, nama_lengkap
◇ Diamond Relationship Meminjam, Mengembalikan
| (Line) One Satu mahasiswa
O (Crow’s Foot) Many Banyak peminjaman

ERD Final Sistem Perpustakaan

[Mahasiswa]––––––〈Meminjam〉––––––[Buku]
    |1                  |M               |1
    |                   |                |
    |                  [Peminjaman]      |
    |                   |1               |
    |                   |                |
    |                  〈Memiliki〉       |
    |                   |1               |
    |                   |                |
    |                  [Denda]           |

Langkah 2: Normalisasi Database – Optimasi Struktur

Setelah punya ERD, sekarang kita optimasi dengan normalisasi.

Sebelum Normalisasi: Tabel Denormalized

Mari lihat contoh tabel yang tidak terormalisasi:

PeminjamanID NamaMahasiswa Jurusan JudulBuku Pengarang Kategori TanggalPinjam TanggalKembali Denda
001 Andi Wijaya Teknik Informatika Pemrograman Java Budi Santoso Komputer 2024-01-15 2024-01-29 0
002 Budi Santoso Sistem Informasi Database Design Citra Dewi Komputer 2024-01-16 2024-01-30 5000
003 Andi Wijaya Teknik Informatika Algoritma Dasar Dian Pratama Komputer 2024-01-18 2024-02-01 0

Masalah yang Terlihat:

  • Data mahasiswa berulang (Andi Wijaya muncul dua kali)
  • Data buku berulang dalam setiap peminjaman
  • Redundancy data jurusan, kategori, dll.
  • Sulit update data mahasiswa atau buku

First Normal Form (1NF) – Atomic Values

Aturan: Setiap sel harus berisi nilai atomic (tidak bisa dibagi lagi).

Implementasi 1NF:

-- Tabel Mahasiswa
CREATE TABLE Mahasiswa (
    student_id INT PRIMARY KEY,
    nama_lengkap VARCHAR(100) NOT NULL,
    jurusan VARCHAR(50),
    angkatan INT,
    email VARCHAR(100),
    telepon VARCHAR(15)
);

-- Tabel Buku  
CREATE TABLE Buku (
    book_id INT PRIMARY KEY,
    judul_buku VARCHAR(200) NOT NULL,
    pengarang VARCHAR(100),
    penerbit VARCHAR(100),
    tahun_terbit INT,
    kategori VARCHAR(50),
    stok INT DEFAULT 0
);

-- Tabel Peminjaman
CREATE TABLE Peminjaman (
    loan_id INT PRIMARY KEY,
    student_id INT,
    book_id INT,
    tanggal_pinjam DATE NOT NULL,
    tanggal_kembali DATE,
    status_peminjaman VARCHAR(20) DEFAULT 'active',
    FOREIGN KEY (student_id) REFERENCES Mahasiswa(student_id),
    FOREIGN KEY (book_id) REFERENCES Buku(book_id)
);

Second Normal Form (2NF) – Eliminasi Partial Dependency

Aturan: Sudah memenuhi 1NF + semua non-key attributes fully functionally dependent pada primary key.

Analisis 2NF pada Tabel Peminjaman:

-- Primary Key Peminjaman: loan_id
-- Attributes: student_id, book_id, tanggal_pinjam, tanggal_kembali, status

-- Tidak ada partial dependency karena:
-- Semua attributes bergantung pada loan_id (primary key)
-- Jadi sudah memenuhi 2NF

Third Normal Form (3NF) – Eliminasi Transitive Dependency

Aturan: Sudah memenuhi 2NF + tidak ada transitive dependency.

Contoh Transitive Dependency:

-- Jika kita punya attribute "kode_jurusan" dan "nama_jurusan" di tabel Mahasiswa:
student_id → kode_jurusan → nama_jurusan

-- Ini transitive dependency karena:
-- nama_jurusan bergantung pada kode_jurusan
-- kode_jurusan bergantung pada student_id

Solusi 3NF:

-- Pisahkan menjadi dua tabel:

-- Tabel Jurusan
CREATE TABLE Jurusan (
    kode_jurusan VARCHAR(10) PRIMARY KEY,
    nama_jurusan VARCHAR(100) NOT NULL,
    fakultas VARCHAR(100)
);

-- Update Tabel Mahasiswa
CREATE TABLE Mahasiswa (
    student_id INT PRIMARY KEY,
    nama_lengkap VARCHAR(100) NOT NULL,
    kode_jurusan VARCHAR(10),
    angkatan INT,
    email VARCHAR(100),
    telepon VARCHAR(15),
    FOREIGN KEY (kode_jurusan) REFERENCES Jurusan(kode_jurusan)
);

Langkah 3: Implementasi SQL dari ERD dan Normalisasi

Mari implementasikan desain kita ke SQL commands.

SQL Script Lengkap

-- Buat database
CREATE DATABASE perpustakaan_universitas;
USE perpustakaan_universitas;

-- Tabel Jurusan (3NF)
CREATE TABLE Jurusan (
    kode_jurusan VARCHAR(10) PRIMARY KEY,
    nama_jurusan VARCHAR(100) NOT NULL,
    fakultas VARCHAR(100)
);

-- Tabel Mahasiswa (3NF)
CREATE TABLE Mahasiswa (
    student_id INT PRIMARY KEY AUTO_INCREMENT,
    nim VARCHAR(20) UNIQUE NOT NULL,
    nama_lengkap VARCHAR(100) NOT NULL,
    kode_jurusan VARCHAR(10),
    angkatan INT,
    email VARCHAR(100) UNIQUE,
    telepon VARCHAR(15),
    alamat TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (kode_jurusan) REFERENCES Jurusan(kode_jurusan)
);

-- Tabel Kategori Buku (3NF)
CREATE TABLE Kategori (
    kategori_id INT PRIMARY KEY AUTO_INCREMENT,
    nama_kategori VARCHAR(50) NOT NULL,
    deskripsi TEXT
);

-- Tabel Buku (3NF)
CREATE TABLE Buku (
    book_id INT PRIMARY KEY AUTO_INCREMENT,
    isbn VARCHAR(20) UNIQUE,
    judul_buku VARCHAR(200) NOT NULL,
    pengarang VARCHAR(100),
    penerbit VARCHAR(100),
    tahun_terbit INT,
    kategori_id INT,
    stok INT DEFAULT 0,
    rak_lokasi VARCHAR(50),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (kategori_id) REFERENCES Kategori(kategori_id)
);

-- Tabel Peminjaman (2NF)
CREATE TABLE Peminjaman (
    loan_id INT PRIMARY KEY AUTO_INCREMENT,
    student_id INT NOT NULL,
    book_id INT NOT NULL,
    tanggal_pinjam DATE NOT NULL,
    tanggal_jatuh_tempo DATE NOT NULL,
    tanggal_kembali DATE,
    status_peminjaman ENUM('active', 'returned', 'overdue') DEFAULT 'active',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (student_id) REFERENCES Mahasiswa(student_id),
    FOREIGN KEY (book_id) REFERENCES Buku(book_id),
    INDEX idx_student (student_id),
    INDEX idx_book (book_id),
    INDEX idx_status (status_peminjaman)
);

-- Tabel Denda (3NF)
CREATE TABLE Denda (
    fine_id INT PRIMARY KEY AUTO_INCREMENT,
    loan_id INT UNIQUE NOT NULL,
    jumlah_denda DECIMAL(10,2) DEFAULT 0,
    status_bayar ENUM('paid', 'unpaid') DEFAULT 'unpaid',
    tanggal_bayar DATE,
    keterangan TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (loan_id) REFERENCES Peminjaman(loan_id)
);

-- Tabel Admin (untuk kelola sistem)
CREATE TABLE Admin (
    admin_id INT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50) UNIQUE NOT NULL,
    password_hash VARCHAR(255) NOT NULL,
    nama_lengkap VARCHAR(100),
    level_akses ENUM('superadmin', 'admin') DEFAULT 'admin',
    last_login TIMESTAMP NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Insert Sample Data

-- Insert data Jurusan
INSERT INTO Jurusan (kode_jurusan, nama_jurusan, fakultas) VALUES
('TI', 'Teknik Informatika', 'Fakultas Ilmu Komputer'),
('SI', 'Sistem Informasi', 'Fakultas Ilmu Komputer'),
('MI', 'Manajemen Informatika', 'Fakultas Ilmu Komputer');

-- Insert data Kategori
INSERT INTO Kategori (nama_kategori, deskripsi) VALUES
('Komputer', 'Buku-buku tentang teknologi komputer dan pemrograman'),
('Matematika', 'Buku matematika dasar dan lanjutan'),
('Fiksi', 'Novel dan karya fiksi lainnya');

-- Insert data Mahasiswa
INSERT INTO Mahasiswa (nim, nama_lengkap, kode_jurusan, angkatan, email) VALUES
('20240001', 'Andi Wijaya', 'TI', 2024, 'andi@university.ac.id'),
('20240002', 'Budi Santoso', 'SI', 2024, 'budi@university.ac.id'),
('20240003', 'Citra Dewi', 'MI', 2024, 'citra@university.ac.id');

-- Insert data Buku
INSERT INTO Buku (isbn, judul_buku, pengarang, penerbit, tahun_terbit, kategori_id, stok) VALUES
('978-1234567890', 'Pemrograman Java untuk Pemula', 'Budi Santoso', 'Penerbit Informatika', 2023, 1, 5),
('978-0987654321', 'Database Design Fundamentals', 'Citra Dewi', 'Penerbit Komputer', 2022, 1, 3),
('978-1122334455', 'Matematika Diskrit', 'Dian Pratama', 'Penerbit Edukasi', 2021, 2, 7);

-- Insert data Peminjaman
INSERT INTO Peminjaman (student_id, book_id, tanggal_pinjam, tanggal_jatuh_tempo) VALUES
(1, 1, '2024-01-15', '2024-01-29'),
(2, 2, '2024-01-16', '2024-01-30'),
(1, 3, '2024-01-18', '2024-02-01');

Langkah 4: Query dan Laporan dari Database Terormalisasi

Mari lihat keuntungan database terormalisasi melalui query yang powerful.

Contoh Query yang Efisien

-- 1. Daftar peminjaman aktif dengan detail lengkap
SELECT 
    m.nama_lengkap AS mahasiswa,
    j.nama_jurusan AS jurusan,
    b.judul_buku AS buku,
    k.nama_kategori AS kategori,
    p.tanggal_pinjam,
    p.tanggal_jatuh_tempo,
    DATEDIFF(CURDATE(), p.tanggal_jatuh_tempo) AS hari_terlambat
FROM Peminjaman p
JOIN Mahasiswa m ON p.student_id = m.student_id
JOIN Jurusan j ON m.kode_jurusan = j.kode_jurusan
JOIN Buku b ON p.book_id = b.book_id
JOIN Kategori k ON b.kategori_id = k.kategori_id
WHERE p.status_peminjaman = 'active';

-- 2. Statistik peminjaman per jurusan
SELECT 
    j.nama_jurusan AS jurusan,
    COUNT(p.loan_id) AS total_peminjaman,
    COUNT(CASE WHEN p.status_peminjaman = 'active' THEN 1 END) AS sedang_dipinjam,
    COUNT(CASE WHEN p.status_peminjaman = 'overdue' THEN 1 END) AS terlambat
FROM Jurusan j
LEFT JOIN Mahasiswa m ON j.kode_jurusan = m.kode_jurusan
LEFT JOIN Peminjaman p ON m.student_id = p.student_id
GROUP BY j.kode_jurusan, j.nama_jurusan
ORDER BY total_peminjaman DESC;

-- 3. Buku paling populer
SELECT 
    b.judul_buku AS buku,
    b.pengarang,
    k.nama_kategori AS kategori,
    COUNT(p.loan_id) AS jumlah_peminjaman,
    b.stok
FROM Buku b
JOIN Kategori k ON b.kategori_id = k.kategori_id
LEFT JOIN Peminjaman p ON b.book_id = p.book_id
GROUP BY b.book_id, b.judul_buku, b.pengarang, k.nama_kategori, b.stok
ORDER BY jumlah_peminjaman DESC
LIMIT 10;

-- 4. Denda yang belum dibayar
SELECT 
    m.nama_lengkap AS mahasiswa,
    m.email,
    b.judul_buku AS buku,
    p.tanggal_pinjam,
    p.tanggal_jatuh_tempo,
    d.jumlah_denda,
    DATEDIFF(CURDATE(), p.tanggal_jatuh_tempo) AS hari_terlambat
FROM Denda d
JOIN Peminjaman p ON d.loan_id = p.loan_id
JOIN Mahasiswa m ON p.student_id = m.student_id
JOIN Buku b ON p.book_id = b.book_id
WHERE d.status_bayar = 'unpaid'
AND p.tanggal_jatuh_tempo < CURDATE();

Tools untuk Membuat ERD dan Normalisasi

1. ERD Tools Populer

Tool Platform Fitur Unggulan Harga
Lucidchart Web-based Collaboration, templates Freemium
draw.io (Diagrams.net) Web/Desktop Free, integration Free
MySQL Workbench Desktop MySQL integration Free
Microsoft Visio Desktop Enterprise features Paid

2. Database Design Tools

  • dbdiagram.io – Tool online untuk membuat ERD dari code
  • SchemaSpy – Generate documentation dari database existing
  • DataGrip – IDE database dengan tools design integrated

Best Practices ERD dan Normalisasi

1. Naming Convention yang Konsisten

-- ✅ Good naming
table: students, books, categories
column: student_id, book_title, created_at

-- ❌ Avoid
table: tbl_student, Book_Table, CATEGORIES
column: StudentID, bookTitle, Created_At

2. Normalisasi yang Bijak (Jangan Over-normalize)

  • Normalize sampai 3NF untuk kebanyakan aplikasi
  • Pertimbangkan denormalization untuk reporting atau read-heavy applications
  • Balance antara normalization dan performance

3. Documentasi yang Baik

-- Selalu tambahkan comments pada tabel dan kolom
CREATE TABLE Students (
    student_id INT PRIMARY KEY COMMENT 'Unique identifier for student',
    email VARCHAR(100) UNIQUE COMMENT 'Student email for login and communication',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'Record creation timestamp'
) COMMENT 'Table storing student information';

Common Mistakes dan Cara Menghindarinya

1. Terlalu Banyak atau Sedikit Tabel

Problem: Over-normalization menghasilkan terlalu banyak join

Solution: Ikuti 3NF sebagai standard, denormalize jika diperlukan

2. Tidak Mempertimbangkan Performance

Problem: Design hanya fokus pada normalization tanpa pertimbangan query performance

Solution: Test query dengan data volume realistic selama design phase

3. Mengabaikan Future Requirements

Problem: Design terlalu rigid untuk kebutuhan future

Solution: Buat design yang flexible dan extensible

Case Study: Evolusi Database E-commerce

Mari lihat contoh evolusi database e-commerce dari denormalized ke normalized.

Version 1: Denormalized (Masalah)

-- Single table approach (BAD)
CREATE TABLE orders_denormalized (
    order_id INT,
    customer_name VARCHAR(100),
    customer_email VARCHAR(100),
    product_name VARCHAR(200),
    product_category VARCHAR(50),
    quantity INT,
    price DECIMAL(10,2),
    order_date DATE
);

Version 2: Normalized (Solution)

-- Normalized structure (GOOD)
CREATE TABLE customers (...);
CREATE TABLE products (...);
CREATE TABLE categories (...);
CREATE TABLE orders (...);
CREATE TABLE order_items (...);

Kesimpulan: Seni Mendesain Database yang Optimal

Menguasai ERD dan normalisasi database adalah skill fundamental untuk siapa pun yang bekerja dengan data. Dengan pendekatan yang terstruktur:

  • ERD memberikan clarity dan visual understanding
  • Normalisasi memastikan data integrity dan efficiency
  • Kombinasi keduanya menghasilkan database yang robust dan maintainable

Ingat, database design yang baik bukan tentang mengikuti rules secara membabi buta, tapi tentang memahami trade-offs dan membuat keputusan yang informed. Practice dengan project nyata, belajar dari mistakes, dan terus berkembang!