SQL Quest

Lessons, hints, explained solutions, the in-browser PostgreSQL editor and practice answer checks in your browser are available. Graded assessments, recorded progress from answer checks and new certificates aren't available yet.

Nakheel Hotels bookings

Nakheel Hotels (fictional) · 7 tables · 35,882 rows · version 1

Open in the playground

Two years (2024–2025) of hotel bookings plus reservations already made for early 2026: hotels, room types, rooms, about 4,000 guests, ~15,000 bookings (stays, cancellations and no-shows), extra services consumed during stays and guest reviews. Built for hospitality analytics: occupancy, ADR and RevPAR, seasonality (Ramadan and Hajj in Makkah and Madinah, summer in Abha and Taif, Riyadh Season), channel mix, cancellations, repeat guests and review scores. Amounts are in SAR.

The documentation is open to everyone. Querying this dataset in the playground is included with Formula Quest membership; lessons that use it follow their own access.

Relationships

hotels7PKhotel_idhotel_namecitystar_ratingrooms_countopened_onroom_types24PKroom_type_idFKhotel_idroom_type_namecapacitybase_raterooms69PKroom_idFKhotel_idFKroom_type_idroom_numberfloorout_of_service_sinceguests3,968PKguest_idfirst_namelast_nameemailnationality_grouployalty_tiercreated_atbookings15,356PKbooking_idFKguest_idFKhotel_idFKroom_type_idFKroom_idbooked_atcheck_incheck_outadults+ 6 more columnsbooking_services12,800PKbooking_service_idFKbooking_idservice_typeservice_dateamountreviews3,658PKreview_idFKbooking_idratingposted_on
Arrows point from a foreign key to the table it references. PK = primary key, FK = foreign key. Numbers are row counts.

Sample queries

Bookings by status and channel
SELECT channel, status, count(*) AS bookings
FROM bookings
GROUP BY channel, status
ORDER BY channel, bookings DESC;
Room-nights and room revenue by month (nights allocated to the month they fall in)
SELECT date_trunc('month', n.night)::date AS month,
       count(*) AS room_nights,
       sum(b.nightly_rate) AS room_revenue
FROM bookings b
CROSS JOIN LATERAL generate_series(b.check_in, b.check_out - 1, interval '1 day') AS n(night)
WHERE b.status IN ('checked_out', 'checked_in')
  AND n.night < DATE '2026-01-01'
GROUP BY 1
ORDER BY 1;
ADR by hotel and channel (completed stays)
SELECT h.hotel_name, b.channel,
       round(sum(b.total_amount) / sum(b.check_out - b.check_in), 2) AS adr
FROM bookings b
JOIN hotels h ON h.hotel_id = b.hotel_id
WHERE b.status = 'checked_out'
GROUP BY 1, 2
ORDER BY 1, 2;
Cancellation rate by channel
SELECT channel,
       count(*) AS bookings,
       round(100.0 * count(*) FILTER (WHERE status = 'cancelled') / count(*), 1) AS cancel_pct
FROM bookings
WHERE check_in <= DATE '2025-12-31'
GROUP BY channel
ORDER BY cancel_pct DESC;
Average review rating by hotel
SELECT h.hotel_name, count(*) AS reviews, round(avg(r.rating), 2) AS avg_rating
FROM reviews r
JOIN bookings b ON b.booking_id = r.booking_id
JOIN hotels h ON h.hotel_id = b.hotel_id
GROUP BY h.hotel_name
ORDER BY avg_rating DESC;

Data dictionary

hotels

7 rows · primary key (hotel_id)

Hotels in the group.

ColumnTypeNull?KeyDescription
hotel_idintegerno PKUnique hotel number.
hotel_nametextnouniqueHotel name.
citytextnoCity (Mecca = Makkah, Medina = Madinah).
star_ratingsmallintnoOfficial star rating.
rooms_countintegernoNumber of rooms built (including rooms out of service).
opened_ondatenoOpening date (Taif opened during the data period).

room_types

24 rows · primary key (room_type_id)

Room categories offered at each hotel, with the published base rate.

ColumnTypeNull?KeyDescription
room_type_idintegerno PKUnique room type number.
hotel_idintegerno hotels.hotel_idHotel.
room_type_nametextnostandard, deluxe, suite or family.
capacityintegernoMaximum guests (adults + children).
base_ratenumeric(12,2)noPublished base rate per night in SAR; actual rates move with demand and channel.

rooms

69 rows · primary key (room_id)

Physical rooms.

ColumnTypeNull?KeyDescription
room_idintegerno PKUnique room number across the group.
hotel_idintegerno hotels.hotel_idHotel.
room_type_idintegerno room_types.room_type_idRoom type of this room.
room_numbertextnoNumber on the door (floor + sequence).
floorsmallintnoFloor.
out_of_service_sincedateyesDate the room was taken out of service (renovation). NULL = in service.

guests

3,968 rows · primary key (guest_id)

Guest profiles. One person can have more than one profile.

ColumnTypeNull?KeyDescription
guest_idintegerno PKUnique guest profile number.
first_nametextnoFirst name (fictional).
last_nametextnoFamily name (fictional).
emailtextyesEmail address. NULL when not given; not unique (duplicate profiles share it).
nationality_grouptextnoBroad nationality group (Saudi, GCC, Other Arab, South Asian, Southeast Asian, East Asian, European, Other).
loyalty_tiertextyessilver, gold or platinum. NULL = not a loyalty member.
created_attimestampnoWhen the profile was created.

bookings

15,356 rows · primary key (booking_id)

Reservations. A booking covers the nights from check_in up to (not including) check_out, at one nightly rate.

ColumnTypeNull?KeyDescription
booking_idintegerno PKUnique booking number (ascending with booking time).
guest_idintegerno guests.guest_idGuest who made the booking.
hotel_idintegerno hotels.hotel_idHotel.
room_type_idintegerno room_types.room_type_idRoom type booked (and paid for).
room_idintegeryes rooms.room_idRoom assigned at check-in. NULL for cancelled, no-show and future (confirmed) bookings.
booked_attimestampnoWhen the booking was made.
check_indatenoArrival date (first night).
check_outdatenoDeparture date (the night before is the last night).
adultsintegernoNumber of adults.
childrenintegernoNumber of children.
channeltextnodirect (website/phone), ota (online travel agency), corporate (company agreement, discounted) or walk_in.
statustextnoStatus on 2025-12-31: confirmed (future arrival), checked_in (in house), checked_out, cancelled or no_show.
nightly_ratenumeric(12,2)noRoom rate per night in SAR (0 = complimentary).
total_amountnumeric(12,2)nonightly_rate × nights: the booked room value. Not revenue for cancelled or no-show bookings. Excludes services.
cancelled_attimestampyesWhen the booking was cancelled. NULL unless status = 'cancelled'.

booking_services

12,800 rows · primary key (booking_service_id)

Extra services charged to a stay (dining, spa, laundry, airport transfers).

ColumnTypeNull?KeyDescription
booking_service_idintegerno PKUnique charge number.
booking_idintegerno bookings.booking_idThe stay charged.
service_typetextnodining, spa, laundry or airport_transfer.
service_datedatenoDate of the charge (within the stay).
amountnumeric(12,2)noAmount charged in SAR.

reviews

3,658 rows · primary key (review_id)

Guest reviews, at most one per stay.

ColumnTypeNull?KeyDescription
review_idintegerno PKUnique review number.
booking_idintegerno bookings.booking_iduniqueThe stay reviewed.
ratingsmallintnoOverall rating from 1 (worst) to 10 (best).
posted_ondatenoDate the review was posted.