w3resource

SQL Projects: Hotel Booking System - Database Design and Management

Hotel Booking System:

The Hotel Booking System is a comprehensive solution to manage hotel reservations, room availability, guest information, payments, and staff records. This system allows efficient handling of bookings, check-ins, check-outs, and payment tracking. This project will cover database design, table creation, inserting sample data, implementing core functionalities, and providing SQL queries for generating insights.

Creating the Database in MySQL or PostgreSQL:

Create the Database:


-- Create the database for the Movie Rental System
CREATE DATABASE HotelBookingDB;
USE HotelBookingDB;

Create the Tables:

Rooms Table:

The Rooms table is designed to store information about hotel rooms. It includes a unique identifier for each room, its type (e.g., Single or Suite), the nightly price, and availability status, indicating whether the room is available for booking.

Structure:

Column Name Data Type Constraints
room_id INT(PK) Unique identifier for each room
room_type VARCHAR(50) Type of room (e.g., Single, Suite)
price DECIMAL(10, 2) Price per night
is_available BOOLEAN Availability status

Code:

-- Create the Rooms table
CREATE TABLE Rooms (
    room_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,  -- Unique room identifier
    room_type VARCHAR(50) NOT NULL,          -- Type of room
    price DECIMAL(10, 2) NOT NULL,           -- Price per night
    is_available BOOLEAN DEFAULT TRUE        -- Room availability status
);

Guests Table :

The Guests table stores essential information about each hotel guest. It includes a unique guest identifier, the guest's full name, email address (which must be unique), contact number, and the date they registered, helping manage guest details effectively.

Structure:

Column Name Data Type Constraints
guest_id INT (PK) Unique identifier for each guest
name VARCHAR(25) Full name of the guest
email VARCHAR(25) Email address
phone VARCHAR(15) Contact number
join_date DATE Date the guest registered

Code:

-- Create the Guests table
CREATE TABLE Guests (
    guest_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,  -- Unique identifier for each guest
    name VARCHAR(25) NOT NULL,               -- Full name of the guest
    email VARCHAR(25) UNIQUE,                -- Email address (must be unique)
    phone VARCHAR(15),                        -- Contact number
    join_date DATE DEFAULT (CURRENT_DATE)     -- Guest join date
);

Bookings Table:

The Bookings table holds information on each hotel booking. It includes a unique booking ID, references to the guest and room, check-in and check-out dates, and the total amount charged. This table links guests with their booked rooms, ensuring accurate reservation management.

Structure:

Column Name Data Type Constraints
booking_id INT (PK) Unique booking identifier
guest_id INT (FK) Guest associated with the booking
room_id INT (FK) Room associated with the booking
check_in DATE Check-in date
check_out DATE Check-out date
total_amount DECIMAL(10, 2) Total amount for the booking

Code:

-- Create the Bookings table
CREATE TABLE Bookings (
    booking_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,   -- Unique identifier for each booking
    guest_id INT,                                -- Foreign key to Guests table
    room_id INT,                                 -- Foreign key to Rooms table
    check_in DATE NOT NULL,                      -- Check-in date
    check_out DATE NOT NULL,                     -- Check-out date
    total_amount DECIMAL(10, 2) NOT NULL,        -- Total amount for the booking
    FOREIGN KEY (guest_id) REFERENCES Guests(guest_id), -- Linking to Guests
    FOREIGN KEY (room_id) REFERENCES Rooms(room_id)     -- Linking to Rooms
);

Payments Table:

The Payments table records payment details for each booking. It includes a unique payment ID, links to the booking, the amount paid, the payment date, and the status (Paid or Unpaid). This table helps manage and track payment information for reservations efficiently.

Structure:

Column Name Data Type Constraints
payment_id INT (PK) Unique identifier for each payment
booking_id INT (FK) Associated booking
amount_paid DECIMAL(10, 2) Amount paid
payment_date DATE Date of payment
payment_status ENUM Status of payment (Paid/Unpaid)

Code:

-- Create the Payments table
CREATE TABLE Payments (
    payment_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,  -- Unique identifier for each payment
    booking_id INT,                             -- Foreign key to Bookings table
    amount_paid DECIMAL(10, 2) NOT NULL,        -- Amount paid
    payment_date DATE DEFAULT (CURRENT_DATE),   -- Payment date
    payment_status ENUM('Paid', 'Unpaid') DEFAULT 'Unpaid', -- Payment status
    FOREIGN KEY (booking_id) REFERENCES Bookings(booking_id) -- Link to Bookings
);

Staff Table:

The Staff table stores information about hotel staff members. It includes a unique staff ID, the staff member's full name, and their role (e.g., Manager or Receptionist). This table helps manage staff records and their responsibilities within the hotel.

Structure:

Column Name Data Type Constraints
staff_id INT (PK) Unique identifier for each staff member
name VARCHAR(255) Staff member's full name
role VARCHAR(100) Role (e.g., Manager, Receptionist)

Code:

-- Create the Staff table
CREATE TABLE Staff (
    staff_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,  -- Unique identifier for each staff member
    name VARCHAR(25) NOT NULL,               -- Full name of the staff member
    role VARCHAR(100)                         -- Role of the staff member
);

Inserting Data:

Add some sample data to the tables.

Inserting Data into Rooms Table:

-- Insert rooms into Rooms table
INSERT INTO Rooms (room_type, price) VALUES
('Single', 100.00),
('Double', 150.00),
('Suite', 300.00);

Inserting Data into Guests Table:

-- Insert guests into Guests table
INSERT INTO Guests (name, email, phone) VALUES
('Paul Adam', '[email protected]', '1234567890'),
('Devon McGraw', '[email protected]', '0987654321');

Inserting Data into Bookings Table:

-- Insert booking records into Bookings table
INSERT INTO Bookings (guest_id, room_id, check_in, check_out, total_amount) VALUES
(1, 1, '2024-11-01', '2024-11-05', 400.00),
(2, 2, '2024-11-10', '2024-11-15', 750.00);

Inserting Data into Payments Table:

-- Insert payments into Payments table
INSERT INTO Payments (booking_id, amount_paid, payment_status) VALUES
(1, 400.00, 'Paid'),
(2, 750.00, 'Paid');

Inserting Data into Staff Table:

-- Insert staff into Staff table
INSERT INTO Staff (name, role) VALUES
('Stain Hill', 'Manager'),
('Steave Harris', 'Receptionist');

Basic Functionalities:

  • Add New Bookings
  • Register New Guests
  • Process Payments
  • Assign Rooms
  • Track Room Availability
  • Check-in and Check-out
  • Generate Reports for Revenue and Booking History

Writing Queries for Functionality:

Query-1: List Available Rooms

SELECT *                  -- Selects all columns
FROM Rooms                -- Specifies the Rooms table as the data source
WHERE is_available = TRUE; -- Filters results to include only available rooms (where is_available is true)

Explanation:

This query retrieves all rooms from the Rooms table that are currently available for booking. By setting the is_available condition to TRUE, it ensures that only rooms marked as available appear in the result set.

Output:

room_id	room_type	price	is_available
1	Single	100.00	1
2	Double	150.00	1
3	Suite	300.00	1

Query-2: Get Booking Details for a Guest

SELECT *                    -- Selects all columns
FROM Bookings               -- Specifies the Bookings table as the data source
WHERE guest_id = 1;         -- Filters results to show only bookings for the guest with ID 1

Explanation:

This query fetches all booking records associated with a specific guest, identified by guest_id = 1, from the Bookings table. It retrieves details like booking dates, room information, and status,

Output:

booking_id	guest_id	room_id	check_in	check_out	total_amount
1	1	1	2024-11-01	2024-11-05	400.00

Query-3: Total Revenue from Bookings

SELECT SUM(total_amount) AS total_revenue  -- Calculates the sum of all total_amount values, labeling it as total_revenue
FROM Bookings;                             -- Specifies the Bookings table as the data source

Explanation:

This query calculates the total revenue generated from all bookings by summing up the total_amount for each record in the Bookings table. The result is labeled total_revenue, providing a quick overview of income from bookings.

Output:

total_revenue
1150.00

Query-4: Room Occupancy Report

SELECT room_type,                      -- Selects the type of room from the Rooms table
       COUNT(*) AS occupancy_count     -- Counts the number of bookings per room type and labels it as occupancy_count
FROM Bookings                          -- Specifies the Bookings table as the main data source
JOIN Rooms ON Bookings.room_id = Rooms.room_id  -- Joins Rooms table to get room type by matching room_id in both tables
GROUP BY room_type;                    -- Groups the results by room type to get a count for each type

Explanation:

This query generates a report on room occupancy by counting how many times each room type has been booked. It achieves this by joining the Bookings and Rooms tables using room_id, then grouping by room_type. The result gives an occupancy_count for each room type, offering insight into the popularity of different types of rooms.

Output:

room_type	occupancy_count
Single	1
Double	1

Query-5: Payment Status by Booking

	SELECT b.booking_id,                  -- Retrieves the unique ID for each booking
       b.total_amount,                -- Selects the total booking amount from the Bookings table
       p.amount_paid,                 -- Retrieves the amount paid from the Payments table
       p.payment_status               -- Shows the payment status (e.g., Paid, Pending)
FROM Bookings b                       -- Specifies Bookings as the main table with alias 'b'
LEFT JOIN Payments p ON b.booking_id = p.booking_id;  -- Left joins Payments table on matching booking_id values

Explanation:

This query provides a breakdown of payment statuses for each booking by displaying the booking ID, total amount, amount paid, and payment status. It uses a LEFT JOIN between Bookings and Payments on booking_id, ensuring that each booking is shown, even if no payment has been made. This view is useful to identify unpaid or partially paid bookings.

Output:

booking_id	total_amount	amount_paid	payment_status
1	400.00	400.00	Paid
2	750.00	750.00	Paid

Query-6: Calculate Occupancy Rate for Each Room Type

SELECT r.room_type,                           -- Selects the room type from the Rooms table
       COUNT(b.booking_id) AS total_bookings, -- Counts total bookings for each room type
       (COUNT(b.booking_id) / (DATEDIFF(CURDATE(), MIN(b.check_in)))) * 100 AS occupancy_rate
       -- Calculates occupancy rate based on bookings since the earliest check-in date
FROM Rooms r                                  -- Specifies Rooms as the main table with alias 'r'
LEFT JOIN Bookings b ON r.room_id = b.room_id -- Left joins Bookings table to Rooms on matching room_id values
GROUP BY r.room_type;                         -- Groups the results by room type to calculate occupancy per type

Explanation:

This query calculates the occupancy rate for each room type, providing insights into room utilization. It does this by counting bookings for each room type and dividing it by the total number of days since the earliest check-in. The DATEDIFF function calculates the date range, while the division and multiplication by 100 yield the occupancy rate as a percentage.

Output:

room_type	total_bookings	occupancy_rate
Single	1	-25.0000
Double	1	-7.6923
Suite	0	NULL

Query-7: Find All Guests with Multiple Bookings

SELECT g.guest_id,                     -- Selects the unique identifier for each guest
       g.name,                         -- Retrieves the name of each guest
       COUNT(b.booking_id) AS booking_count -- Counts total bookings made by each guest
FROM Guests g                          -- Specifies Guests as the main table with alias 'g'
JOIN Bookings b ON g.guest_id = b.guest_id -- Joins Bookings table to Guests on matching guest_id values
GROUP BY g.guest_id                    -- Groups results by guest_id to aggregate bookings per guest
HAVING booking_count > 1;              -- Filters results to only show guests with more than one booking

Explanation:

This query identifies guests who have made multiple bookings, providing the guest's ID, name, and the total number of bookings they've made. By joining the Bookings table with Guests on guest_id and grouping by each guest, we can use HAVING to filter for those with a booking_count greater than one.

Output:

Output not generated for insufficient data

Query-8: Calculate Average Booking Duration by Room Type

SELECT r.room_type,                              -- Selects the room type for each room
       AVG(DATEDIFF(b.check_out, b.check_in)) AS avg_duration -- Calculates average stay duration by room type
FROM Rooms r                                     -- Specifies Rooms as the main table with alias 'r'
JOIN Bookings b ON r.room_id = b.room_id         -- Joins Bookings table to Rooms on matching room_id values
GROUP BY r.room_type;                            -- Groups results by room type to calculate averages per type

Explanation:

This query calculates the average duration of stay for each room type, providing insights into which room types are booked for longer periods. It uses the DATEDIFF function to find the duration of each booking (difference between check_out and check_in), and then AVG to find the mean duration for each room type. Grouping by room_type allows for results segmented by room category, aiding in understanding booking patterns and preferences.

Output:

room_type	avg_duration
Single	4.0000
Double	5.0000

Query-9: Total Revenue per Room Type

SELECT r.room_type,                                 -- Selects each room type
       SUM(b.total_amount) AS total_revenue         -- Calculates total revenue from bookings per room type
FROM Rooms r                                        -- Specifies Rooms as the primary table with alias 'r'
JOIN Bookings b ON r.room_id = b.room_id            -- Joins Bookings table to Rooms on matching room_id values
GROUP BY r.room_type;                               -- Groups results by room type to summarize revenue per type

Explanation:

This query calculates the total revenue generated for each room type. By joining the Rooms and Bookings tables on the common room_id, it accesses total_amount from each booking, then sums up this value for each room type. Grouping by room_type provides aggregated revenue information by category.

Output:

room_type	total_revenue
Single	400.00
Double	750.00

Query-10: List All Overdue Check-Outs (Where Check-Out Date is Past and Room is Still Marked as Booked)

SELECT b.booking_id,                          -- Retrieves the unique booking ID
       g.name,                                 -- Retrieves guest's name associated with the booking
       b.check_out,                            -- Retrieves check-out date from the booking
       r.room_type                             -- Retrieves the type of room for the booking
FROM Bookings b                                -- Specifies Bookings as the main table with alias 'b'
JOIN Guests g ON b.guest_id = g.guest_id       -- Joins Guests table to access guest names using guest_id
JOIN Rooms r ON b.room_id = r.room_id          -- Joins Rooms table to get room details using room_id
WHERE b.check_out < CURDATE()                  -- Filters for bookings where check-out date is in the past
  AND r.is_available = FALSE;                  -- Filters for rooms still marked as unavailable (booked)

Explanation:

This query identifies all overdue check-outs by listing bookings where the scheduled check-out date has already passed, but the room remains marked as unavailable (indicating it’s still booked). The query joins the Bookings, Guests, and Rooms tables to retrieve booking ID, guest name, check-out date, and room type for these cases.

Output:

Output not generated for insufficient data

Query-11: Identify High-Spending Guests (Guests Who Have Spent More Than a Given Amount)

SELECT g.guest_id,                         -- Retrieves the unique guest ID
       g.name,                             -- Retrieves guest name
       SUM(b.total_amount) AS total_spent  -- Calculates total spending by summing up the total amount per guest
FROM Guests g                              -- Specifies Guests as the main table with alias 'g'
JOIN Bookings b ON g.guest_id = b.guest_id -- Joins Bookings table to get booking details using guest_id
GROUP BY g.guest_id                        -- Groups results by each guest's unique ID
HAVING total_spent > 1000;                 -- Filters for guests whose total spending exceeds 1000

Explanation:

This query identifies high-spending guests by calculating each guest's total spending based on the bookings they have made. By summing the total_amount for each guest and using a HAVING clause, it filters guests who have spent more than a specified amount (in this case, 1000). The result displays each high-spending guest’s ID, name, and total spending, which helps in identifying valuable guests for potential loyalty programs or special offers.

Output:

Output not generated for insufficient data

Query-12: Get Upcoming Check-Ins for the Next 7 Days

SELECT b.booking_id,               -- Retrieves unique ID for each booking
       g.name,                     -- Retrieves guest's name
       b.check_in,                 -- Retrieves check-in date for each booking
       r.room_type                 -- Retrieves type of room booked
FROM Bookings b                    -- Specifies Bookings table as the main table with alias 'b'
JOIN Guests g ON b.guest_id = g.guest_id  -- Joins Guests table to retrieve guest details using guest_id
JOIN Rooms r ON b.room_id = r.room_id     -- Joins Rooms table to retrieve room details using room_id
WHERE b.check_in BETWEEN CURDATE() AND DATE_ADD(CURDATE(), INTERVAL 7 DAY); -- Filters for check-ins within the next 7 days

Explanation:

This query lists upcoming check-ins scheduled within the next seven days. Using the WHERE clause, it filters for bookings with a check_in date between today (CURDATE()) and seven days from today (DATE_ADD(CURDATE(), INTERVAL 7 DAY)). The result includes the booking ID, guest’s name, check-in date, and room type, making it useful for preparing the hotel for expected arrivals within the week.

Output:

booking_id	name	check_in	room_type
1	Paul Adam	2024-11-01	Single

Query-13: Generate Monthly Revenue Report for the Current Year

SELECT MONTH(b.check_in) AS month,           -- Retrieves the month from check-in dates, labeled as 'month'
       SUM(b.total_amount) AS monthly_revenue -- Calculates total revenue for each month
FROM Bookings b                               -- Specifies Bookings table as the main table with alias 'b'
WHERE YEAR(b.check_in) = YEAR(CURDATE())      -- Filters records to include only the current year based on check-in dates
GROUP BY month                                -- Groups results by month to get revenue totals per month
ORDER BY month;                               -- Orders the results in chronological order by month

Explanation:

This query produces a monthly revenue report for the current year, focusing on the check_in date in each booking. The MONTH() function extracts the month from each check_in date, while SUM(b.total_amount) aggregates the booking amounts to calculate total monthly revenue. Grouped by month and ordered chronologically, the report is ideal for tracking revenue trends across months within the current year.

Output:

month	monthly_revenue
11	1150.00

Query-14: List Rooms that Have Not Been Booked in the Past 6 Months

SELECT r.room_id, r.room_type                  -- Selects room ID and room type to display rooms
FROM Rooms r                                   -- Specifies Rooms table as the main table with alias 'r'
LEFT JOIN Bookings b ON r.room_id = b.room_id  -- Performs a LEFT JOIN between Rooms and Bookings tables on room ID
     AND b.check_in >= DATE_SUB(CURDATE(), INTERVAL 6 MONTH) -- Filters bookings to those with check-in dates within the last 6 months
WHERE b.booking_id IS NULL;                    -- Filters to only show rooms without bookings in the last 6 months

Explanation:

This query identifies rooms that have not been booked in the last 6 months. By using a LEFT JOIN between the Rooms and Bookings tables, the query retrieves all rooms while only including bookings made within the last 6 months. The WHERE clause filters out any rows where a matching booking record exists, thereby displaying only rooms with no recent bookings.

Output:

room_id	room_type
3	Suite

Query-15: Find Most Popular Room Type Based on Bookings

SELECT room_type, COUNT(*) AS bookings_count  -- Selects room type and counts bookings per room type
FROM Bookings b                               -- Specifies Bookings table as the main table with alias 'b'
JOIN Rooms r ON b.room_id = r.room_id         -- Joins Rooms table on room ID to get room type information
GROUP BY r.room_type                          -- Groups results by room type to count bookings per type
ORDER BY bookings_count DESC                  -- Orders results by booking count in descending order
LIMIT 1;                                      -- Limits results to the top 1 room type with the highest booking count

Explanation:

This query finds the most popular room type based on the number of bookings. By joining the Bookings table with the Rooms table on room ID, it retrieves the room type for each booking. The GROUP BY clause groups bookings by room type, and COUNT(*) calculates the number of bookings for each type. The ORDER BY clause sorts the results by booking count in descending order, while LIMIT 1 restricts the output to the top result, providing the room type with the highest booking frequency. This insight can help identify which room type is in highest demand.

Output:

room_type	bookings_count
Single	1

Query-16: Calculate Total Unpaid Amounts by Booking ID

SELECT b.booking_id,  -- Selects the booking ID
       (b.total_amount - COALESCE(SUM(p.amount_paid), 0)) AS unpaid_amount  -- Calculates the unpaid amount by subtracting the total paid amount from the total amount due
FROM Bookings b  -- Specifies Bookings table as the main table with alias 'b'
LEFT JOIN Payments p ON b.booking_id = p.booking_id  -- Left joins Payments table on booking ID to include all bookings, even those without payments
GROUP BY b.booking_id  -- Groups results by booking ID to aggregate payment amounts
HAVING unpaid_amount > 0;  -- Filters results to only include bookings with an unpaid amount greater than zero

Explanation:

This query calculates the total unpaid amounts for each booking in the hotel system. It starts by selecting the booking_id from the Bookings table and computes the unpaid amount by subtracting the total paid amount (summed from the Payments table) from the total_amount due for each booking. The COALESCE function ensures that if there are no associated payments for a booking, it treats the sum as zero. By grouping the results by booking_id, it aggregates the payment information, and the HAVING clause filters the results to show only those bookings that have an unpaid amount greater than zero.

Output:

Output not generated for insufficient data

Query-17: Identify Top 5 Guests by Spending

SELECT g.guest_id,  -- Selects the guest ID
       g.name,  -- Selects the guest's name
       SUM(b.total_amount) AS total_spent  -- Calculates the total amount spent by each guest
FROM Guests g  -- Specifies Guests table with alias 'g'
JOIN Bookings b ON g.guest_id = b.guest_id  -- Joins Bookings table on guest ID to link each booking to a guest
GROUP BY g.guest_id  -- Groups results by guest ID to aggregate total spending per guest
ORDER BY total_spent DESC  -- Orders guests by total amount spent in descending order
LIMIT 5;  -- Limits the results to the top 5 guests

Explanation:

This query identifies the top 5 guests in terms of total spending on bookings. By joining the Guests table with the Bookings table based on guest_id, the query associates each guest with their booking records. It then calculates the total spending for each guest using SUM(b.total_amount), and groups the results by guest_id to get a total amount spent per guest. The ORDER BY total_spent DESC sorts the results in descending order of total spending, and LIMIT 5 restricts the output to the top 5 guests with the highest spending.

Output:

guest_id	name	total_spent
2	Devon McGraw	750.00
1	Paul Adam	400.00

Query-18: Calculate Room Occupancy for Each Month

SELECT MONTH(b.check_in) AS month,  -- Extracts the month from check-in date
       r.room_type,  -- Selects the room type
       COUNT(b.booking_id) AS occupied_days  -- Counts the number of bookings as occupied days
FROM Bookings b  -- Specifies Bookings table with alias 'b'
JOIN Rooms r ON b.room_id = r.room_id  -- Joins Rooms table on room ID to associate each booking with its room type
GROUP BY month, r.room_type  -- Groups results by month and room type for monthly occupancy per room type
ORDER BY month;  -- Orders the output by month for a sequential view of occupancy over the year

Explanation:

This query calculates the monthly occupancy for each room type, helping track how many days each type of room was booked each month. By joining the Bookings and Rooms tables, it links each booking to its respective room type. The MONTH(b.check_in) function extracts the month from each booking’s check-in date, and COUNT(b.booking_id) counts the number of bookings, representing the days a room type was occupied in that month. Grouping by both month and room_type gives occupancy counts for each room type monthly, and ordering by month presents results in chronological order.

Output:

month	room_type	occupied_days
11	Double	1
11	Single	1

Query-19: Find All Payments with Late Payments Status (Payments Made After Check-Out)

SELECT p.payment_id,  -- Retrieves the unique payment identifier
       b.booking_id,  -- Retrieves the booking ID associated with the payment
       g.name,  -- Retrieves the guest's name associated with the booking
       p.payment_date,  -- Shows the date when the payment was made
       b.check_out  -- Shows the scheduled check-out date for the booking
FROM Payments p  -- Specifies Payments table with alias 'p'
JOIN Bookings b ON p.booking_id = b.booking_id  -- Joins Bookings table on booking ID to link payments with their bookings
JOIN Guests g ON b.guest_id = g.guest_id  -- Joins Guests table to include guest details
WHERE p.payment_date > b.check_out;  -- Filters for payments made after the check-out date, indicating late payment status

Explanation:

This query identifies payments made after the scheduled check-out date, indicating potential late payments. By joining the Payments, Bookings, and Guests tables, the query provides detailed information about each late payment, including the payment ID, booking ID, guest name, payment date, and the original check-out date. The WHERE clause filters records where the payment_date is later than the check_out date.

Output:

Output not generated for insufficient data

SQL Code Editor:




Become a Patron!

Follow us on Facebook and Twitter for latest update.

It will be nice if you may share this link in any developer community or anywhere else, from where other developers may find this content. Thanks.

https://w3resource.com/projects/sql/sql-projects-on-hotel-booking-system.php