Contoh ERD Pemesanan Reservasi Kamar Hotel – Tutorial Lengkap Database Design

By | August 16, 2025

Dalam pengembangan sistem informasi hotel, contoh ERD pemesanan reservasi kamar hotel merupakan fondasi penting yang harus dipahami oleh setiap database designer dan system analyst. Entity Relationship Diagram (ERD) untuk sistem hotel tidak hanya menggambarkan struktur data, tetapi juga memodelkan proses bisnis kompleks yang terjadi dalam operasional hotel sehari-hari. Artikel ini akan membahas secara komprehensif cara merancang ERD untuk sistem pemesanan dan reservasi kamar hotel, lengkap dengan penjelasan entity, relationship, atribut, dan implementasi praktis dalam database.

Table of Contents

Konsep Dasar ERD untuk Sistem Hotel

Sebelum merancang ERD, penting untuk memahami proses bisnis dan kebutuhan sistem reservasi hotel secara menyeluruh.

🏨 Analisis Proses Bisnis Hotel

Alur Proses Reservasi Hotel:

  1. Guest Registration: Tamu mendaftar atau login ke sistem
  2. Room Search: Pencarian kamar berdasarkan kriteria
  3. Room Selection: Pemilihan tipe kamar dan fasilitas
  4. Booking Process: Proses pemesanan dengan detail lengkap
  5. Payment Processing: Pembayaran dan konfirmasi
  6. Check-in/Check-out: Proses kedatangan dan keberangkatan
  7. Service Management: Layanan tambahan selama menginap

Stakeholder Utama:

  • Guest/Tamu: Pelanggan yang melakukan reservasi
  • Front Office Staff: Petugas resepsionis
  • Housekeeping: Petugas kebersihan kamar
  • Manager: Pengelola hotel
  • Finance: Bagian keuangan dan billing
💡 Pro Tip: Untuk memahami konsep database design secara mendalam, pelajari tutorial database design dari dasar yang membahas fundamental modeling mulai dari conceptual hingga physical design.

Identifikasi Entity Utama Sistem Hotel

Langkah pertama dalam merancang ERD adalah mengidentifikasi entity-entity utama yang terlibat dalam sistem reservasi hotel.

🎯 Entity Utama Sistem Hotel

1. GUEST (Tamu)

guest_id (PK) – ID unik tamu

first_name – Nama depan

last_name – Nama belakang

email – Email address

phone – Nomor telepon

address – Alamat lengkap

id_number – Nomor identitas

nationality – Kewarganegaraan

date_of_birth – Tanggal lahir

created_at – Tanggal registrasi

2. ROOM_TYPE (Tipe Kamar)

room_type_id (PK) – ID tipe kamar

type_name – Nama tipe (Standard, Deluxe, Suite)

description – Deskripsi fasilitas

base_price – Harga dasar per malam

max_occupancy – Kapasitas maksimal

bed_type – Jenis tempat tidur

room_size – Ukuran kamar (m²)

amenities – Fasilitas kamar

3. ROOM (Kamar)

room_id (PK) – ID kamar

room_type_id (FK) – Referensi tipe kamar

room_number – Nomor kamar

floor – Lantai

status – Status kamar (Available, Occupied, Maintenance)

view_type – Jenis pemandangan

last_cleaned – Terakhir dibersihkan

4. RESERVATION (Reservasi)

reservation_id (PK) – ID reservasi

guest_id (FK) – Referensi tamu

room_id (FK) – Referensi kamar

check_in_date – Tanggal check-in

check_out_date – Tanggal check-out

adults – Jumlah dewasa

children – Jumlah anak

total_amount – Total biaya

status – Status reservasi

special_requests – Permintaan khusus

created_at – Tanggal booking

5. PAYMENT (Pembayaran)

payment_id (PK) – ID pembayaran

reservation_id (FK) – Referensi reservasi

payment_method – Metode pembayaran

amount – Jumlah bayar

payment_date – Tanggal pembayaran

transaction_id – ID transaksi

status – Status pembayaran

notes – Catatan pembayaran

Relationship dan Cardinality

Setelah mengidentifikasi entity, langkah selanjutnya adalah menentukan relationship dan cardinality antar entity.

🔗 Relationship Mapping

1. GUEST – RESERVATION

Relationship: Makes

Cardinality: 1:M (One-to-Many)

Penjelasan: Satu tamu dapat membuat banyak reservasi, tetapi satu reservasi hanya dimiliki oleh satu tamu.

Business Rule: Tamu dapat melakukan multiple booking untuk tanggal yang berbeda.

2. ROOM_TYPE – ROOM

Relationship: Has

Cardinality: 1:M (One-to-Many)

Penjelasan: Satu tipe kamar dapat memiliki banyak kamar fisik, tetapi satu kamar hanya memiliki satu tipe.

Business Rule: Hotel memiliki multiple kamar dengan tipe yang sama (contoh: 10 kamar Standard).

3. ROOM – RESERVATION

Relationship: Reserved_for

Cardinality: 1:M (One-to-Many)

Penjelasan: Satu kamar dapat direservasi berkali-kali (waktu berbeda), tetapi satu reservasi hanya untuk satu kamar.

Business Rule: Kamar dapat dibooking untuk periode yang berbeda-beda.

4. RESERVATION – PAYMENT

Relationship: Paid_by

Cardinality: 1:M (One-to-Many)

Penjelasan: Satu reservasi dapat memiliki multiple pembayaran (DP, pelunasan), tetapi satu pembayaran hanya untuk satu reservasi.

Business Rule: Pembayaran dapat dilakukan secara bertahap (installment).

📊 Visualisasi ERD Sederhana

GUEST
1:M
RESERVATION
M:1
ROOM

 

ROOM_TYPE
1:M
ROOM

 

RESERVATION
1:M
PAYMENT
🎯 Advanced Database: Untuk mempelajari konsep normalisasi dan optimasi database yang lebih advanced, ikuti kelas normalisasi database yang membahas teknik optimasi performa database.

Entity Tambahan untuk Sistem Lengkap

Untuk sistem hotel yang lebih komprehensif, diperlukan entity tambahan yang mendukung operasional hotel secara menyeluruh.

🏗️ Entity Pendukung Sistem Hotel

6. EMPLOYEE (Karyawan)

employee_id (PK) – ID karyawan

employee_code – Kode karyawan

first_name – Nama depan

last_name – Nama belakang

position – Jabatan

department – Departemen

email – Email karyawan

phone – Nomor telepon

hire_date – Tanggal masuk kerja

salary – Gaji

status – Status aktif/non-aktif

7. SERVICE (Layanan)

service_id (PK) – ID layanan

service_name – Nama layanan

description – Deskripsi layanan

price – Harga layanan

category – Kategori (F&B, Spa, Laundry)

availability – Ketersediaan

duration – Durasi layanan

8. RESERVATION_SERVICE (Layanan Reservasi)

reservation_service_id (PK) – ID layanan reservasi

reservation_id (FK) – Referensi reservasi

service_id (FK) – Referensi layanan

quantity – Jumlah layanan

service_date – Tanggal layanan

total_price – Total harga

status – Status layanan

notes – Catatan khusus

9. HOUSEKEEPING (Housekeeping)

housekeeping_id (PK) – ID housekeeping

room_id (FK) – Referensi kamar

employee_id (FK) – Referensi karyawan

cleaning_date – Tanggal pembersihan

cleaning_type – Jenis pembersihan

start_time – Waktu mulai

end_time – Waktu selesai

status – Status pembersihan

notes – Catatan kondisi kamar

10. RATE (Tarif)

rate_id (PK) – ID tarif

room_type_id (FK) – Referensi tipe kamar

rate_name – Nama tarif

price – Harga per malam

valid_from – Berlaku dari

valid_to – Berlaku sampai

day_type – Jenis hari (Weekday/Weekend)

season – Musim (High/Low/Peak)

is_active – Status aktif

Implementasi SQL Database

Setelah merancang ERD, langkah selanjutnya adalah mengimplementasikan design tersebut ke dalam database SQL.

💾 Script SQL untuk Membuat Database Hotel

— Membuat database hotel
CREATE DATABASE hotel_reservation_system;
USE hotel_reservation_system;– Tabel GUEST
CREATE TABLE guest (
guest_id INT PRIMARY KEY AUTO_INCREMENT,
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
phone VARCHAR(20),
address TEXT,
id_number VARCHAR(50),
nationality VARCHAR(50),
date_of_birth DATE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

— Tabel ROOM_TYPE
CREATE TABLE room_type (
room_type_id INT PRIMARY KEY AUTO_INCREMENT,
type_name VARCHAR(50) NOT NULL,
description TEXT,
base_price DECIMAL(10,2) NOT NULL,
max_occupancy INT NOT NULL,
bed_type VARCHAR(50),
room_size DECIMAL(5,2),
amenities TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

— Tabel ROOM
CREATE TABLE room (
room_id INT PRIMARY KEY AUTO_INCREMENT,
room_type_id INT NOT NULL,
room_number VARCHAR(10) UNIQUE NOT NULL,
floor INT,
status ENUM(‘Available’, ‘Occupied’, ‘Maintenance’, ‘Out of Order’) DEFAULT ‘Available’,
view_type VARCHAR(50),
last_cleaned TIMESTAMP,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (room_type_id) REFERENCES room_type(room_type_id)
);

— Tabel RESERVATION
CREATE TABLE reservation (
reservation_id INT PRIMARY KEY AUTO_INCREMENT,
guest_id INT NOT NULL,
room_id INT NOT NULL,
check_in_date DATE NOT NULL,
check_out_date DATE NOT NULL,
adults INT NOT NULL DEFAULT 1,
children INT DEFAULT 0,
total_amount DECIMAL(10,2) NOT NULL,
status ENUM(‘Pending’, ‘Confirmed’, ‘Checked-in’, ‘Checked-out’, ‘Cancelled’) DEFAULT ‘Pending’,
special_requests TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (guest_id) REFERENCES guest(guest_id),
FOREIGN KEY (room_id) REFERENCES room(room_id)
);

— Tabel PAYMENT
CREATE TABLE payment (
payment_id INT PRIMARY KEY AUTO_INCREMENT,
reservation_id INT NOT NULL,
payment_method ENUM(‘Cash’, ‘Credit Card’, ‘Debit Card’, ‘Bank Transfer’, ‘E-Wallet’) NOT NULL,
amount DECIMAL(10,2) NOT NULL,
payment_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
transaction_id VARCHAR(100),
status ENUM(‘Pending’, ‘Completed’, ‘Failed’, ‘Refunded’) DEFAULT ‘Pending’,
notes TEXT,
FOREIGN KEY (reservation_id) REFERENCES reservation(reservation_id)
);

— Tabel EMPLOYEE
CREATE TABLE employee (
employee_id INT PRIMARY KEY AUTO_INCREMENT,
employee_code VARCHAR(20) UNIQUE NOT NULL,
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL,
position VARCHAR(50) NOT NULL,
department VARCHAR(50) NOT NULL,
email VARCHAR(100) UNIQUE,
phone VARCHAR(20),
hire_date DATE NOT NULL,
salary DECIMAL(10,2),
status ENUM(‘Active’, ‘Inactive’, ‘Terminated’) DEFAULT ‘Active’,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

— Tabel SERVICE
CREATE TABLE service (
service_id INT PRIMARY KEY AUTO_INCREMENT,
service_name VARCHAR(100) NOT NULL,
description TEXT,
price DECIMAL(10,2) NOT NULL,
category VARCHAR(50),
availability BOOLEAN DEFAULT TRUE,
duration INT, — dalam menit
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

— Tabel RESERVATION_SERVICE (Many-to-Many relationship)
CREATE TABLE reservation_service (
reservation_service_id INT PRIMARY KEY AUTO_INCREMENT,
reservation_id INT NOT NULL,
service_id INT NOT NULL,
quantity INT NOT NULL DEFAULT 1,
service_date DATE,
total_price DECIMAL(10,2) NOT NULL,
status ENUM(‘Requested’, ‘Confirmed’, ‘Completed’, ‘Cancelled’) DEFAULT ‘Requested’,
notes TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (reservation_id) REFERENCES reservation(reservation_id),
FOREIGN KEY (service_id) REFERENCES service(service_id)
);

— Tabel HOUSEKEEPING
CREATE TABLE housekeeping (
housekeeping_id INT PRIMARY KEY AUTO_INCREMENT,
room_id INT NOT NULL,
employee_id INT NOT NULL,
cleaning_date DATE NOT NULL,
cleaning_type ENUM(‘Checkout Cleaning’, ‘Maintenance Cleaning’, ‘Deep Cleaning’) NOT NULL,
start_time TIME,
end_time TIME,
status ENUM(‘Scheduled’, ‘In Progress’, ‘Completed’) DEFAULT ‘Scheduled’,
notes TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (room_id) REFERENCES room(room_id),
FOREIGN KEY (employee_id) REFERENCES employee(employee_id)
);

— Tabel RATE
CREATE TABLE rate (
rate_id INT PRIMARY KEY AUTO_INCREMENT,
room_type_id INT NOT NULL,
rate_name VARCHAR(100) NOT NULL,
price DECIMAL(10,2) NOT NULL,
valid_from DATE NOT NULL,
valid_to DATE NOT NULL,
day_type ENUM(‘Weekday’, ‘Weekend’, ‘Holiday’) DEFAULT ‘Weekday’,
season ENUM(‘Low’, ‘High’, ‘Peak’) DEFAULT ‘Low’,
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (room_type_id) REFERENCES room_type(room_type_id)
);

🔧 Index untuk Optimasi Performa:

— Membuat index untuk optimasi query
CREATE INDEX idx_guest_email ON guest(email);
CREATE INDEX idx_room_number ON room(room_number);
CREATE INDEX idx_reservation_dates ON reservation(check_in_date, check_out_date);
CREATE INDEX idx_reservation_status ON reservation(status);
CREATE INDEX idx_payment_date ON payment(payment_date);
CREATE INDEX idx_rate_dates ON rate(valid_from, valid_to);
CREATE INDEX idx_housekeeping_date ON housekeeping(cleaning_date);– Composite index untuk query kompleks
CREATE INDEX idx_room_status_type ON room(status, room_type_id);
CREATE INDEX idx_reservation_guest_dates ON reservation(guest_id, check_in_date, check_out_date);

Sample Data dan Query Testing

Untuk menguji design database, kita perlu memasukkan sample data dan menjalankan query testing.

📊 Insert Sample Data

— Insert sample room types
INSERT INTO room_type (type_name, description, base_price, max_occupancy, bed_type, room_size, amenities) VALUES
(‘Standard’, ‘Kamar standar dengan fasilitas dasar’, 500000, 2, ‘Double Bed’, 25.00, ‘AC, TV, WiFi, Kamar Mandi Dalam’),
(‘Deluxe’, ‘Kamar deluxe dengan fasilitas lengkap’, 750000, 2, ‘Queen Bed’, 35.00, ‘AC, TV LCD, WiFi, Mini Bar, Balkon’),
(‘Suite’, ‘Suite mewah dengan ruang tamu terpisah’, 1200000, 4, ‘King Bed + Sofa Bed’, 50.00, ‘AC, Smart TV, WiFi, Mini Bar, Jacuzzi, Balkon’),
(‘Family’, ‘Kamar keluarga dengan 2 tempat tidur’, 900000, 4, ‘2 Double Beds’, 40.00, ‘AC, TV, WiFi, Mini Fridge, Connecting Door’);– Insert sample rooms
INSERT INTO room (room_type_id, room_number, floor, status, view_type) VALUES
(1, ‘101’, 1, ‘Available’, ‘Garden View’),
(1, ‘102’, 1, ‘Available’, ‘Garden View’),
(1, ‘103’, 1, ‘Occupied’, ‘Garden View’),
(2, ‘201’, 2, ‘Available’, ‘City View’),
(2, ‘202’, 2, ‘Available’, ‘City View’),
(2, ‘203’, 2, ‘Maintenance’, ‘City View’),
(3, ‘301’, 3, ‘Available’, ‘Ocean View’),
(3, ‘302’, 3, ‘Available’, ‘Ocean View’),
(4, ‘401’, 4, ‘Available’, ‘Mountain View’),
(4, ‘402’, 4, ‘Occupied’, ‘Mountain View’);

— Insert sample guests
INSERT INTO guest (first_name, last_name, email, phone, address, id_number, nationality, date_of_birth) VALUES
(‘John’, ‘Doe’, ‘john.doe@email.com’, ‘+62812345678’, ‘Jl. Sudirman No. 123, Jakarta’, ‘3171234567890123’, ‘Indonesian’, ‘1985-05-15’),
(‘Jane’, ‘Smith’, ‘jane.smith@email.com’, ‘+62823456789’, ‘Jl. Thamrin No. 456, Jakarta’, ‘3171234567890124’, ‘Indonesian’, ‘1990-08-22’),
(‘Michael’, ‘Johnson’, ‘michael.j@email.com’, ‘+1234567890’, ‘123 Main St, New York, USA’, ‘P123456789’, ‘American’, ‘1982-12-10’),
(‘Sarah’, ‘Wilson’, ‘sarah.w@email.com’, ‘+447123456789’, ‘456 Oxford St, London, UK’, ‘UK987654321’, ‘British’, ‘1988-03-18’);

— Insert sample reservations
INSERT INTO reservation (guest_id, room_id, check_in_date, check_out_date, adults, children, total_amount, status, special_requests) VALUES
(1, 1, ‘2024-02-15’, ‘2024-02-18’, 2, 0, 1500000, ‘Confirmed’, ‘Late check-in around 10 PM’),
(2, 4, ‘2024-02-20’, ‘2024-02-23’, 2, 1, 2250000, ‘Confirmed’, ‘Extra bed for child’),
(3, 7, ‘2024-02-25’, ‘2024-02-28’, 2, 0, 3600000, ‘Pending’, ‘Honeymoon package’),
(4, 9, ‘2024-03-01’, ‘2024-03-05’, 4, 2, 3600000, ‘Confirmed’, ‘Connecting rooms if available’);

— Insert sample payments
INSERT INTO payment (reservation_id, payment_method, amount, transaction_id, status, notes) VALUES
(1, ‘Credit Card’, 750000, ‘TXN001234567’, ‘Completed’, ‘Down payment 50%’),
(2, ‘Bank Transfer’, 1125000, ‘TXN001234568’, ‘Completed’, ‘Down payment 50%’),
(3, ‘Credit Card’, 1800000, ‘TXN001234569’, ‘Completed’, ‘Down payment 50%’),
(4, ‘Cash’, 3600000, ‘TXN001234570’, ‘Completed’, ‘Full payment’);

— Insert sample services
INSERT INTO service (service_name, description, price, category, availability, duration) VALUES
(‘Room Service – Breakfast’, ‘Sarapan diantar ke kamar’, 150000, ‘Food & Beverage’, TRUE, 30),
(‘Laundry Service’, ‘Layanan cuci dan setrika pakaian’, 50000, ‘Laundry’, TRUE, 1440),
(‘Spa Treatment’, ‘Perawatan spa dan massage’, 300000, ‘Spa & Wellness’, TRUE, 90),
(‘Airport Transfer’, ‘Antar jemput bandara’, 200000, ‘Transportation’, TRUE, 60),
(‘Extra Bed’, ‘Tempat tidur tambahan’, 100000, ‘Room Amenity’, TRUE, 0);

— Insert sample employees
INSERT INTO employee (employee_code, first_name, last_name, position, department, email, phone, hire_date, salary) VALUES
(‘EMP001’, ‘Ahmad’, ‘Santoso’, ‘Front Office Manager’, ‘Front Office’, ‘ahmad.s@hotel.com’, ‘+62811111111’, ‘2020-01-15’, 8000000),
(‘EMP002’, ‘Siti’, ‘Nurhaliza’, ‘Receptionist’, ‘Front Office’, ‘siti.n@hotel.com’, ‘+62822222222’, ‘2021-03-10’, 4500000),
(‘EMP003’, ‘Budi’, ‘Prasetyo’, ‘Housekeeping Supervisor’, ‘Housekeeping’, ‘budi.p@hotel.com’, ‘+62833333333’, ‘2019-06-20’, 6000000),
(‘EMP004’, ‘Rina’, ‘Wati’, ‘Room Attendant’, ‘Housekeeping’, ‘rina.w@hotel.com’, ‘+62844444444’, ‘2022-01-05’, 3500000);

🔍 Query Testing dan Validasi

1. Query untuk Cek Ketersediaan Kamar:

SELECT
rt.type_name,
r.room_number,
rt.base_price,
r.view_type
FROM room r
JOIN room_type rt ON r.room_type_id = rt.room_type_id
WHERE r.status = ‘Available’
AND r.room_id NOT IN (
SELECT room_id
FROM reservation
WHERE status IN (‘Confirmed’, ‘Checked-in’)
AND (
(‘2024-02-20’ BETWEEN check_in_date AND check_out_date) OR
(‘2024-02-23’ BETWEEN check_in_date AND check_out_date) OR
(check_in_date BETWEEN ‘2024-02-20’ AND ‘2024-02-23’)
)
)
ORDER BY rt.base_price;

2. Query untuk Laporan Reservasi Harian:

SELECT
res.reservation_id,
CONCAT(g.first_name, ‘ ‘, g.last_name) AS guest_name,
r.room_number,
rt.type_name,
res.check_in_date,
res.check_out_date,
res.total_amount,
res.status
FROM reservation res
JOIN guest g ON res.guest_id = g.guest_id
JOIN room r ON res.room_id = r.room_id
JOIN room_type rt ON r.room_type_id = rt.room_type_id
WHERE DATE(res.created_at) = CURDATE()
ORDER BY res.created_at DESC;

3. Query untuk Revenue Analysis:

SELECT
DATE_FORMAT(p.payment_date, ‘%Y-%m’) AS month,
COUNT(DISTINCT res.reservation_id) AS total_reservations,
SUM(p.amount) AS total_revenue,
AVG(p.amount) AS average_payment
FROM payment p
JOIN reservation res ON p.reservation_id = res.reservation_id
WHERE p.status = ‘Completed’
AND p.payment_date >= DATE_SUB(CURDATE(), INTERVAL 12 MONTH)
GROUP BY DATE_FORMAT(p.payment_date, ‘%Y-%m’)
ORDER BY month DESC;

4. Query untuk Room Occupancy Rate:

SELECT
rt.type_name,
COUNT(r.room_id) AS total_rooms,
COUNT(CASE WHEN r.status = ‘Occupied’ THEN 1 END) AS occupied_rooms,
ROUND(
(COUNT(CASE WHEN r.status = ‘Occupied’ THEN 1 END) * 100.0 / COUNT(r.room_id)), 2
) AS occupancy_rate_percent
FROM room r
JOIN room_type rt ON r.room_type_id = rt.room_type_id
GROUP BY rt.room_type_id, rt.type_name
ORDER BY occupancy_rate_percent DESC;

Normalisasi Database dan Optimasi

Untuk memastikan database design yang optimal, perlu dilakukan normalisasi dan optimasi struktur data.

📐 Analisis Normalisasi Database

🔍 First Normal Form (1NF):

  • ✅ Semua atribut memiliki nilai atomic (tidak dapat dibagi lagi)
  • ✅ Tidak ada repeating groups dalam satu record
  • ✅ Setiap record memiliki primary key yang unik
  • ✅ Tidak ada duplikasi data dalam satu tabel

🔍 Second Normal Form (2NF):

  • ✅ Sudah memenuhi 1NF
  • ✅ Semua non-key attributes fully dependent pada primary key
  • ✅ Tidak ada partial dependency pada composite key
  • ✅ Tabel junction (reservation_service) sudah proper

🔍 Third Normal Form (3NF):

  • ✅ Sudah memenuhi 2NF
  • ✅ Tidak ada transitive dependency
  • ✅ Non-key attributes tidak bergantung pada non-key attributes lain
  • ✅ Referential integrity terjaga dengan foreign keys

⚠️ Potential Denormalization untuk Performance:

Pertimbangan Denormalization:

  • Guest Name dalam Reservation: Untuk query reporting yang sering
  • Room Type Name dalam Room: Untuk mengurangi JOIN operations
  • Total Amount Calculation: Pre-calculated untuk performa
  • Occupancy Status: Redundant field untuk quick filtering

⚡ Optimasi Performa Database

1. Indexing Strategy:

— Primary indexes (sudah otomatis)
— Secondary indexes untuk query optimization
CREATE INDEX idx_reservation_checkin ON reservation(check_in_date);
CREATE INDEX idx_reservation_checkout ON reservation(check_out_date);
CREATE INDEX idx_reservation_status ON reservation(status);
CREATE INDEX idx_room_status ON room(status);
CREATE INDEX idx_payment_status ON payment(status);
CREATE INDEX idx_guest_email ON guest(email);– Composite indexes untuk complex queries
CREATE INDEX idx_reservation_dates_status ON reservation(check_in_date, check_out_date, status);
CREATE INDEX idx_room_type_status ON room(room_type_id, status);
CREATE INDEX idx_payment_reservation_status ON payment(reservation_id, status);

2. Partitioning Strategy:

— Partitioning tabel reservation berdasarkan tahun
ALTER TABLE reservation
PARTITION BY RANGE (YEAR(check_in_date)) (
PARTITION p2023 VALUES LESS THAN (2024),
PARTITION p2024 VALUES LESS THAN (2025),
PARTITION p2025 VALUES LESS THAN (2026),
PARTITION p_future VALUES LESS THAN MAXVALUE
);– Partitioning tabel payment berdasarkan bulan
ALTER TABLE payment
PARTITION BY RANGE (YEAR(payment_date) * 100 + MONTH(payment_date)) (
PARTITION p202401 VALUES LESS THAN (202402),
PARTITION p202402 VALUES LESS THAN (202403),
PARTITION p202403 VALUES LESS THAN (202404),
PARTITION p_current VALUES LESS THAN MAXVALUE
);

3. Query Optimization Tips:

  • Use LIMIT: Batasi hasil query untuk pagination
  • Avoid SELECT *: Pilih kolom yang diperlukan saja
  • Use EXISTS: Lebih efisien daripada IN untuk subquery
  • Proper JOIN: Gunakan INNER JOIN jika memungkinkan
  • Index Hints: Gunakan USE INDEX jika diperlukan
  • Query Cache: Aktifkan query caching untuk query berulang
🚀 Performance Monitoring: Untuk mempelajari teknik monitoring dan tuning database MySQL yang advanced, ikuti kelas MySQL performance tuning yang membahas profiling, optimization, dan scaling strategies.

Implementasi Business Logic dan Constraints

Selain struktur data, perlu diimplementasikan business rules dan constraints untuk menjaga integritas data.

🔒 Database Constraints dan Triggers

1. Check Constraints untuk Business Rules:

— Constraint untuk memastikan check-out setelah check-in
ALTER TABLE reservation
ADD CONSTRAINT chk_checkout_after_checkin
CHECK (check_out_date > check_in_date);– Constraint untuk memastikan adults minimal 1
ALTER TABLE reservation
ADD CONSTRAINT chk_adults_minimum
CHECK (adults >= 1);

— Constraint untuk memastikan children tidak negatif
ALTER TABLE reservation
ADD CONSTRAINT chk_children_positive
CHECK (children >= 0);

— Constraint untuk memastikan total amount positif
ALTER TABLE reservation
ADD CONSTRAINT chk_total_amount_positive
CHECK (total_amount > 0);

— Constraint untuk memastikan payment amount positif
ALTER TABLE payment
ADD CONSTRAINT chk_payment_amount_positive
CHECK (amount > 0);

— Constraint untuk memastikan room number unik per floor
ALTER TABLE room
ADD CONSTRAINT uk_room_number_floor
UNIQUE (room_number, floor);

2. Triggers untuk Automated Business Logic:

— Trigger untuk update room status saat check-in
DELIMITER //
CREATE TRIGGER tr_reservation_checkin
AFTER UPDATE ON reservation
FOR EACH ROW
BEGIN
IF NEW.status = ‘Checked-in’ AND OLD.status != ‘Checked-in’ THEN
UPDATE room
SET status = ‘Occupied’
WHERE room_id = NEW.room_id;
END IF;
END//– Trigger untuk update room status saat check-out
CREATE TRIGGER tr_reservation_checkout
AFTER UPDATE ON reservation
FOR EACH ROW
BEGIN
IF NEW.status = ‘Checked-out’ AND OLD.status != ‘Checked-out’ THEN
UPDATE room
SET status = ‘Available’
WHERE room_id = NEW.room_id;

— Schedule housekeeping
INSERT INTO housekeeping (room_id, employee_id, cleaning_date, cleaning_type, status)
SELECT NEW.room_id,
(SELECT employee_id FROM employee WHERE department = ‘Housekeeping’ AND status = ‘Active’ LIMIT 1),
CURDATE(),
‘Checkout Cleaning’,
‘Scheduled’;
END IF;
END//

— Trigger untuk calculate total amount
CREATE TRIGGER tr_calculate_total_amount
BEFORE INSERT ON reservation
FOR EACH ROW
BEGIN
DECLARE room_price DECIMAL(10,2);
DECLARE nights INT;

SELECT rt.base_price INTO room_price
FROM room r
JOIN room_type rt ON r.room_type_id = rt.room_type_id
WHERE r.room_id = NEW.room_id;

SET nights = DATEDIFF(NEW.check_out_date, NEW.check_in_date);
SET NEW.total_amount = room_price * nights;
END//

— Trigger untuk prevent double booking
CREATE TRIGGER tr_prevent_double_booking
BEFORE INSERT ON reservation
FOR EACH ROW
BEGIN
DECLARE booking_count INT;

SELECT COUNT(*) INTO booking_count
FROM reservation
WHERE room_id = NEW.room_id
AND status IN (‘Confirmed’, ‘Checked-in’)
AND (
(NEW.check_in_date BETWEEN check_in_date AND check_out_date) OR
(NEW.check_out_date BETWEEN check_in_date AND check_out_date) OR
(check_in_date BETWEEN NEW.check_in_date AND NEW.check_out_date)
);

IF booking_count > 0 THEN
SIGNAL SQLSTATE ‘45000’
SET MESSAGE_TEXT = ‘Room is already booked for the selected dates’;
END IF;
END//

DELIMITER ;

3. Stored Procedures untuk Complex Operations:

— Stored procedure untuk check room availability
DELIMITER //
CREATE PROCEDURE sp_check_room_availability(
IN p_check_in DATE,
IN p_check_out DATE,
IN p_room_type_id INT
)
BEGIN
SELECT
r.room_id,
r.room_number,
rt.type_name,
rt.base_price,
r.view_type
FROM room r
JOIN room_type rt ON r.room_type_id = rt.room_type_id
WHERE r.status = ‘Available’
AND (p_room_type_id IS NULL OR r.room_type_id = p_room_type_id)
AND r.room_id NOT IN (
SELECT room_id
FROM reservation
WHERE status IN (‘Confirmed’, ‘Checked-in’)
AND (
(p_check_in BETWEEN check_in_date AND check_out_date) OR
(p_check_out BETWEEN check_in_date AND check_out_date) OR
(check_in_date BETWEEN p_check_in AND p_check_out)
)
)
ORDER BY rt.base_price;
END//– Stored procedure untuk create reservation
CREATE PROCEDURE sp_create_reservation(
IN p_guest_id INT,
IN p_room_id INT,
IN p_check_in DATE,
IN p_check_out DATE,
IN p_adults INT,
IN p_children INT,
IN p_special_requests TEXT,
OUT p_reservation_id INT
)
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
RESIGNAL;
END;

START TRANSACTION;

— Insert reservation
INSERT INTO reservation (
guest_id, room_id, check_in_date, check_out_date,
adults, children, special_requests, status
) VALUES (
p_guest_id, p_room_id, p_check_in, p_check_out,
p_adults, p_children, p_special_requests, ‘Pending’
);

SET p_reservation_id = LAST_INSERT_ID();

COMMIT;
END//

— Stored procedure untuk generate daily report
CREATE PROCEDURE sp_daily_report(IN p_date DATE)
BEGIN
— Arrivals
SELECT ‘ARRIVALS’ as report_type,
COUNT(*) as count,
SUM(total_amount) as revenue
FROM reservation
WHERE check_in_date = p_date
AND status IN (‘Confirmed’, ‘Checked-in’);

— Departures
SELECT ‘DEPARTURES’ as report_type,
COUNT(*) as count,
SUM(total_amount) as revenue
FROM reservation
WHERE check_out_date = p_date
AND status = ‘Checked-out’;

— In-house guests
SELECT ‘IN_HOUSE’ as report_type,
COUNT(*) as count,
SUM(total_amount) as revenue
FROM reservation
WHERE p_date BETWEEN check_in_date AND check_out_date
AND status = ‘Checked-in’;

— Room occupancy by type
SELECT rt.type_name,
COUNT(r.room_id) as total_rooms,
COUNT(res.room_id) as occupied_rooms,
ROUND((COUNT(res.room_id) * 100.0 / COUNT(r.room_id)), 2) as occupancy_rate
FROM room_type rt
LEFT JOIN room r ON rt.room_type_id = r.room_type_id
LEFT JOIN reservation res ON r.room_id = res.room_id
AND p_date BETWEEN res.check_in_date AND res.check_out_date
AND res.status = ‘Checked-in’
GROUP BY rt.room_type_id, rt.type_name;
END//

DELIMITER ;

Security dan Data Protection

Implementasi security measures untuk melindungi data sensitif dalam sistem hotel.

🔐 Database Security Implementation

1. User Roles dan Privileges:

— Create database users dengan role berbeda
CREATE USER ‘hotel_admin’@’localhost’ IDENTIFIED BY ‘StrongPassword123!’;
CREATE USER ‘front_desk’@’localhost’ IDENTIFIED BY ‘FrontDesk456!’;
CREATE USER ‘housekeeping’@’localhost’ IDENTIFIED BY ‘Housekeep789!’;
CREATE USER ‘finance’@’localhost’ IDENTIFIED BY ‘Finance012!’;
CREATE USER ‘readonly’@’localhost’ IDENTIFIED BY ‘ReadOnly345!’;– Grant privileges untuk admin (full access)
GRANT ALL PRIVILEGES ON hotel_reservation_system.* TO ‘hotel_admin’@’localhost’;

— Grant privileges untuk front desk (reservations, guests, payments)
GRANT SELECT, INSERT, UPDATE ON hotel_reservation_system.guest TO ‘front_desk’@’localhost’;
GRANT SELECT, INSERT, UPDATE ON hotel_reservation_system.reservation TO ‘front_desk’@’localhost’;
GRANT SELECT, INSERT, UPDATE ON hotel_reservation_system.payment TO ‘front_desk’@’localhost’;
GRANT SELECT ON hotel_reservation_system.room TO ‘front_desk’@’localhost’;
GRANT SELECT ON hotel_reservation_system.room_type TO ‘front_desk’@’localhost’;
GRANT SELECT ON hotel_reservation_system.service TO ‘front_desk’@’localhost’;
GRANT SELECT, INSERT, UPDATE ON hotel_reservation_system.reservation_service TO ‘front_desk’@’localhost’;

— Grant privileges untuk housekeeping (rooms, housekeeping)
GRANT SELECT, UPDATE ON hotel_reservation_system.room TO ‘housekeeping’@’localhost’;
GRANT SELECT, INSERT, UPDATE ON hotel_reservation_system.housekeeping TO ‘housekeeping’@’localhost’;
GRANT SELECT ON hotel_reservation_system.reservation TO ‘housekeeping’@’localhost’;

— Grant privileges untuk finance (payments, reports)
GRANT SELECT ON hotel_reservation_system.* TO ‘finance’@’localhost’;
GRANT INSERT, UPDATE ON hotel_reservation_system.payment TO ‘finance’@’localhost’;

— Grant privileges untuk readonly (reports only)
GRANT SELECT ON hotel_reservation_system.* TO ‘readonly’@’localhost’;

FLUSH PRIVILEGES;

2. Data Encryption dan Sensitive Data Protection:

— Encrypt sensitive data (contoh untuk credit card info jika ada)
ALTER TABLE payment ADD COLUMN encrypted_card_info VARBINARY(255);– Function untuk encrypt data
DELIMITER //
CREATE FUNCTION encrypt_sensitive_data(data TEXT)
RETURNS VARBINARY(255)
READS SQL DATA
DETERMINISTIC
BEGIN
RETURN AES_ENCRYPT(data, ‘hotel_encryption_key_2024’);
END//

— Function untuk decrypt data
CREATE FUNCTION decrypt_sensitive_data(encrypted_data VARBINARY(255))
RETURNS TEXT
READS SQL DATA
DETERMINISTIC
BEGIN
RETURN AES_DECRYPT(encrypted_data, ‘hotel_encryption_key_2024’);
END//

DELIMITER ;

— Hash password untuk employee login (jika ada sistem login)
ALTER TABLE employee ADD COLUMN password_hash VARCHAR(255);
ALTER TABLE employee ADD COLUMN salt VARCHAR(32);

— Trigger untuk hash password
DELIMITER //
CREATE TRIGGER tr_hash_employee_password
BEFORE INSERT ON employee
FOR EACH ROW
BEGIN
IF NEW.password_hash IS NOT NULL THEN
SET NEW.salt = SUBSTRING(MD5(RAND()), 1, 32);
SET NEW.password_hash = SHA2(CONCAT(NEW.password_hash, NEW.salt), 256);
END IF;
END//
DELIMITER ;

3. Audit Trail dan Logging:

— Tabel audit untuk tracking changes
CREATE TABLE audit_log (
audit_id INT PRIMARY KEY AUTO_INCREMENT,
table_name VARCHAR(50) NOT NULL,
operation_type ENUM(‘INSERT’, ‘UPDATE’, ‘DELETE’) NOT NULL,
record_id INT NOT NULL,
old_values JSON,
new_values JSON,
user_id VARCHAR(50),
ip_address VARCHAR(45),
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);– Trigger audit untuk reservation
DELIMITER //
CREATE TRIGGER tr_audit_reservation_update
AFTER UPDATE ON reservation
FOR EACH ROW
BEGIN
INSERT INTO audit_log (table_name, operation_type, record_id, old_values, new_values, user_id)
VALUES (
‘reservation’,
‘UPDATE’,
NEW.reservation_id,
JSON_OBJECT(
‘status’, OLD.status,
‘total_amount’, OLD.total_amount,
‘check_in_date’, OLD.check_in_date,
‘check_out_date’, OLD.check_out_date
),
JSON_OBJECT(
‘status’, NEW.status,
‘total_amount’, NEW.total_amount,
‘check_in_date’, NEW.check_in_date,
‘check_out_date’, NEW.check_out_date
),
USER()
);
END//

— Trigger audit untuk payment
CREATE TRIGGER tr_audit_payment_insert
AFTER INSERT ON payment
FOR EACH ROW
BEGIN
INSERT INTO audit_log (table_name, operation_type, record_id, new_values, user_id)
VALUES (
‘payment’,
‘INSERT’,
NEW.payment_id,
JSON_OBJECT(
‘reservation_id’, NEW.reservation_id,
‘amount’, NEW.amount,
‘payment_method’, NEW.payment_method,
‘status’, NEW.status
),
USER()
);
END//

DELIMITER ;

Advanced Features dan Extensions

Pengembangan fitur advanced untuk sistem hotel yang lebih komprehensif.

🚀 Advanced System Features

1. Dynamic Pricing System:

— Tabel untuk dynamic pricing rules
CREATE TABLE pricing_rule (
rule_id INT PRIMARY KEY AUTO_INCREMENT,
rule_name VARCHAR(100) NOT NULL,
room_type_id INT,
season_type ENUM(‘Low’, ‘High’, ‘Peak’) NOT NULL,
day_type ENUM(‘Weekday’, ‘Weekend’, ‘Holiday’) NOT NULL,
occupancy_threshold DECIMAL(5,2), — percentage
price_modifier DECIMAL(5,2) NOT NULL, — multiplier
valid_from DATE NOT NULL,
valid_to DATE NOT NULL,
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (room_type_id) REFERENCES room_type(room_type_id)
);– Function untuk calculate dynamic price
DELIMITER //
CREATE FUNCTION calculate_dynamic_price(
p_room_type_id INT,
p_check_in_date DATE,
p_check_out_date DATE
) RETURNS DECIMAL(10,2)
READS SQL DATA
DETERMINISTIC
BEGIN
DECLARE base_price DECIMAL(10,2);
DECLARE final_price DECIMAL(10,2);
DECLARE current_occupancy DECIMAL(5,2);
DECLARE price_modifier DECIMAL(5,2) DEFAULT 1.0;

— Get base price
SELECT rt.base_price INTO base_price
FROM room_type rt
WHERE rt.room_type_id = p_room_type_id;

— Calculate current occupancy
SELECT
COALESCE(
(COUNT(res.room_id) * 100.0) /
(SELECT COUNT(*) FROM room WHERE room_type_id = p_room_type_id),
0
) INTO current_occupancy
FROM room r
LEFT JOIN reservation res ON r.room_id = res.room_id
AND p_check_in_date BETWEEN res.check_in_date AND res.check_out_date
AND res.status IN (‘Confirmed’, ‘Checked-in’)
WHERE r.room_type_id = p_room_type_id;

— Get price modifier based on rules
SELECT pr.price_modifier INTO price_modifier
FROM pricing_rule pr
WHERE pr.room_type_id = p_room_type_id
AND p_check_in_date BETWEEN pr.valid_from AND pr.valid_to
AND pr.is_active = TRUE
AND (pr.occupancy_threshold IS NULL OR current_occupancy >= pr.occupancy_threshold)
ORDER BY pr.occupancy_threshold DESC
LIMIT 1;

SET final_price = base_price * COALESCE(price_modifier, 1.0);

RETURN final_price;
END//
DELIMITER ;

2. Loyalty Program System:

— Tabel loyalty program
CREATE TABLE loyalty_program (
program_id INT PRIMARY KEY AUTO_INCREMENT,
program_name VARCHAR(100) NOT NULL,
tier_name VARCHAR(50) NOT NULL,
min_points INT NOT NULL,
discount_percentage DECIMAL(5,2),
benefits TEXT,
is_active BOOLEAN DEFAULT TRUE
);– Tabel guest loyalty
CREATE TABLE guest_loyalty (
guest_loyalty_id INT PRIMARY KEY AUTO_INCREMENT,
guest_id INT NOT NULL,
program_id INT NOT NULL,
current_points INT DEFAULT 0,
total_earned_points INT DEFAULT 0,
tier_level VARCHAR(50),
member_since DATE DEFAULT (CURDATE()),
last_activity DATE,
FOREIGN KEY (guest_id) REFERENCES guest(guest_id),
FOREIGN KEY (program_id) REFERENCES loyalty_program(program_id)
);

— Tabel point transactions
CREATE TABLE point_transaction (
transaction_id INT PRIMARY KEY AUTO_INCREMENT,
guest_loyalty_id INT NOT NULL,
reservation_id INT,
transaction_type ENUM(‘Earned’, ‘Redeemed’, ‘Expired’, ‘Bonus’) NOT NULL,
points INT NOT NULL,
description TEXT,
transaction_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (guest_loyalty_id) REFERENCES guest_loyalty(guest_loyalty_id),
FOREIGN KEY (reservation_id) REFERENCES reservation(reservation_id)
);

— Trigger untuk earn points dari reservation
DELIMITER //
CREATE TRIGGER tr_earn_loyalty_points
AFTER UPDATE ON reservation
FOR EACH ROW
BEGIN
DECLARE points_earned INT;
DECLARE guest_loyalty_id INT;

IF NEW.status = ‘Checked-out’ AND OLD.status != ‘Checked-out’ THEN
— Calculate points (1 point per 10,000 spent)
SET points_earned = FLOOR(NEW.total_amount / 10000);

— Get or create guest loyalty record
SELECT gl.guest_loyalty_id INTO guest_loyalty_id
FROM guest_loyalty gl
WHERE gl.guest_id = NEW.guest_id
LIMIT 1;

IF guest_loyalty_id IS NULL THEN
INSERT INTO guest_loyalty (guest_id, program_id, current_points, total_earned_points)
VALUES (NEW.guest_id, 1, points_earned, points_earned);
SET guest_loyalty_id = LAST_INSERT_ID();
ELSE
UPDATE guest_loyalty
SET current_points = current_points + points_earned,
total_earned_points = total_earned_points + points_earned,
last_activity = CURDATE()
WHERE guest_loyalty_id = guest_loyalty_id;
END IF;

— Record point transaction
INSERT INTO point_transaction (guest_loyalty_id, reservation_id, transaction_type, points, description)
VALUES (guest_loyalty_id, NEW.reservation_id, ‘Earned’, points_earned,
CONCAT(‘Points earned from reservation #’, NEW.reservation_id));
END IF;
END//
DELIMITER ;

3. Inventory Management System:

— Tabel inventory items
CREATE TABLE inventory_item (
item_id INT PRIMARY KEY AUTO_INCREMENT,
item_code VARCHAR(50) UNIQUE NOT NULL,
item_name VARCHAR(100) NOT NULL,
category VARCHAR(50),
unit_of_measure VARCHAR(20),
current_stock INT DEFAULT 0,
minimum_stock INT DEFAULT 0,
maximum_stock INT DEFAULT 0,
unit_cost DECIMAL(10,2),
supplier_info TEXT,
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);– Tabel room inventory (items in each room)
CREATE TABLE room_inventory (
room_inventory_id INT PRIMARY KEY AUTO_INCREMENT,
room_id INT NOT NULL,
item_id INT NOT NULL,
quantity INT NOT NULL DEFAULT 0,
last_checked DATE,
condition_status ENUM(‘Good’, ‘Fair’, ‘Poor’, ‘Damaged’) DEFAULT ‘Good’,
notes TEXT,
FOREIGN KEY (room_id) REFERENCES room(room_id),
FOREIGN KEY (item_id) REFERENCES inventory_item(item_id),
UNIQUE KEY uk_room_item (room_id, item_id)
);

— Tabel inventory transactions
CREATE TABLE inventory_transaction (
transaction_id INT PRIMARY KEY AUTO_INCREMENT,
item_id INT NOT NULL,
room_id INT,
transaction_type ENUM(‘Stock In’, ‘Stock Out’, ‘Transfer’, ‘Adjustment’, ‘Damaged’) NOT NULL,
quantity INT NOT NULL,
reference_number VARCHAR(50),
notes TEXT,
employee_id INT,
transaction_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (item_id) REFERENCES inventory_item(item_id),
FOREIGN KEY (room_id) REFERENCES room(room_id),
FOREIGN KEY (employee_id) REFERENCES employee(employee_id)
);

— View untuk low stock alert
CREATE VIEW v_low_stock_alert AS
SELECT
ii.item_id,
ii.item_code,
ii.item_name,
ii.current_stock,
ii.minimum_stock,
(ii.minimum_stock – ii.current_stock) AS shortage_quantity
FROM inventory_item ii
WHERE ii.current_stock <= ii.minimum_stock
AND ii.is_active = TRUE;

🙋‍♂️ Frequently Asked Questions (FAQ)

Q: Bagaimana cara menangani overbooking dalam ERD sistem hotel?
A: Overbooking dapat ditangani dengan beberapa cara: (1) Implementasi trigger untuk mencegah double booking pada tanggal yang sama; (2) Tambahkan tabel waitlist untuk menampung booking cadangan; (3) Buat sistem allocation yang memungkinkan room upgrade otomatis; (4) Implementasi business rule yang memungkinkan overbooking terkontrol dengan persentase tertentu; (5) Tambahkan status ‘Overbooked’ dalam reservation dan sistem notifikasi untuk staff.
Q: Bagaimana cara merancang ERD untuk hotel dengan multiple properties/cabang?
A: Untuk multi-property hotel, tambahkan entity PROPERTY dengan atribut property_id, property_name, address, contact_info. Kemudian tambahkan property_id sebagai foreign key di tabel room, employee, dan entity lain yang terkait lokasi. Implementasikan juga tabel PROPERTY_MANAGER untuk mengelola akses data per property. Pertimbangkan juga database partitioning berdasarkan property untuk performa yang optimal.
Q: Bagaimana cara menangani group booking dan corporate rates dalam ERD?
A: Buat entity GROUP_BOOKING dengan atribut group_id, group_name, contact_person, total_rooms, special_rate. Tambahkan entity CORPORATE_CLIENT untuk perusahaan dengan contract rates. Buat tabel CORPORATE_RATE yang link ke room_type dengan special pricing. Dalam tabel reservation, tambahkan group_id dan corporate_client_id sebagai foreign key optional. Implementasikan juga business logic untuk apply group discounts dan corporate rates.
Q: Bagaimana cara merancang sistem untuk handle room maintenance dan out-of-order?
A: Tambahkan entity MAINTENANCE_REQUEST dengan atribut request_id, room_id, issue_description, priority, status, reported_by, assigned_to. Buat juga tabel MAINTENANCE_LOG untuk tracking history perbaikan. Dalam tabel room, tambahkan status ‘Maintenance’ dan ‘Out of Order’. Implementasikan trigger yang otomatis block room dari booking ketika status berubah ke maintenance. Buat juga sistem scheduling untuk preventive maintenance.
Q: Bagaimana cara implementasi cancellation policy dalam database design?
A: Buat entity CANCELLATION_POLICY dengan atribut policy_id, policy_name, cancellation_hours, penalty_percentage, refund_rules. Link policy ini ke room_type atau rate. Tambahkan tabel CANCELLATION_TRANSACTION untuk record pembatalan dengan penalty calculation. Dalam reservation, tambahkan cancelled_at, cancellation_reason, penalty_amount. Implementasikan stored procedure untuk calculate penalty berdasarkan policy dan timing pembatalan.
Q: Bagaimana cara merancang ERD untuk handle seasonal pricing dan special events?
A: Buat entity SEASON dengan atribut season_id, season_name, start_date, end_date, price_multiplier. Tambahkan entity SPECIAL_EVENT untuk event khusus yang mempengaruhi pricing. Buat tabel SEASONAL_RATE yang menghubungkan room_type dengan season dan special pricing. Implementasikan function calculate_seasonal_price yang mempertimbangkan base price, seasonal multiplier, dan special event surcharge. Gunakan trigger atau stored procedure untuk auto-apply seasonal rates.
Q: Bagaimana cara menangani partial payments dan installment dalam sistem hotel?
A: Dalam tabel payment, tambahkan atribut payment_type (‘Deposit’, ‘Partial’, ‘Full’, ‘Refund’) dan installment_number. Buat tabel PAYMENT_PLAN untuk define payment schedule dengan due_date dan amount per installment. Implementasikan business logic untuk track outstanding balance dan send payment reminders. Tambahkan status ‘Partially Paid’ dalam reservation dan create automated workflow untuk handle payment follow-up.
Q: Bagaimana cara optimize database performance untuk sistem hotel dengan traffic tinggi?
A: Implementasikan beberapa strategi: (1) Database indexing pada kolom yang sering di-query (dates, status, foreign keys); (2) Partitioning tabel besar berdasarkan tanggal atau property; (3) Implement read replicas untuk reporting queries; (4) Use connection pooling dan query caching; (5) Denormalization untuk frequently accessed data; (6) Implement database sharding untuk multi-property; (7) Use stored procedures untuk complex operations; (8) Regular database maintenance dan statistics update.

Kesimpulan

Perancangan contoh ERD pemesanan reservasi kamar hotel merupakan proses kompleks yang memerlukan pemahaman mendalam tentang business process hotel dan database design principles. Melalui artikel ini, kita telah membahas secara komprehensif mulai dari identifikasi entity dasar hingga implementasi advanced features seperti dynamic pricing dan loyalty program.

Poin-poin kunci yang telah dipelajari:

  • Entity Identification: Pengenalan entity utama dan supporting entities
  • Relationship Mapping: Pemahaman cardinality dan business rules
  • Database Implementation: Konversi ERD ke SQL schema yang optimal
  • Business Logic: Implementasi constraints, triggers, dan stored procedures
  • Security Measures: User roles, data encryption, dan audit trails
  • Performance Optimization: Indexing, partitioning, dan query optimization
  • Advanced Features: Dynamic pricing, loyalty program, dan inventory management

Manfaat implementasi ERD hotel yang baik:

  • Meningkatkan efisiensi operasional hotel
  • Mengurangi error dalam proses reservasi
  • Memungkinkan reporting dan analytics yang akurat
  • Mendukung scalability untuk pertumbuhan bisnis
  • Meningkatkan customer experience melalui sistem yang reliable
  • Memfasilitasi integrasi dengan sistem eksternal

🎯 Best Practices untuk Database Hotel:

  • Normalization: Pastikan database design mengikuti normal forms
  • Data Integrity: Implementasi constraints dan validation rules
  • Performance Monitoring: Regular monitoring dan optimization
  • Backup Strategy: Implementasi backup dan disaster recovery plan
  • Security Compliance: Mengikuti standar keamanan data industri
  • Documentation: Maintain comprehensive database documentation
  • Version Control: Track database schema changes
  • Testing: Comprehensive testing untuk semua business scenarios

Dengan menguasai konsep ERD untuk sistem hotel, Anda telah membangun foundation yang solid untuk mengembangkan sistem informasi hotel yang robust dan scalable. Database design yang baik bukan hanya tentang struktur data, tetapi juga tentang memahami business process dan mengantisipasi kebutuhan masa depan. Terus praktikkan dan kembangkan kemampuan database design Anda! 🏨

Leave a Reply

Your email address will not be published. Required fields are marked *