| Version 2 (modified by , 30 hours ago) ( diff ) |
|---|
Advanced Reports
Unit Demand — Quarterly Interest Ranking
Which currently available units show the strongest demand signals in the last quarter, and which buildings are generating the most interest for units. Each unit is scored by the inquiry count, appointment count. The units are ranked within their building, with the goal to give insight into potential buyer interest in what types of flats potential buyers are mostly looking for.
SQL
WITH unit_stats AS (
SELECT
u.unit_id,
u.unit_number,
u.price,
u.room_number,
u.floor_area,
f.floor_number,
b.building_id,
b.name AS building_name,
COUNT(DISTINCT i.inquiry_id) AS total_inquiries,
COUNT(DISTINCT a.appointment_id) AS total_appointments,
ROUND(
COUNT(DISTINCT a.appointment_id)::NUMERIC /
NULLIF(COUNT(DISTINCT i.inquiry_id), 0) * 100, 2
) AS conversion_rate_pct
FROM project.unit u
JOIN project.floor f ON u.floor_id = f.floor_id
JOIN project.building b ON f.building_id = b.building_id
LEFT JOIN project.inquiry i ON u.unit_id = i.unit_id
AND i.created_at >= CURRENT_DATE - INTERVAL '3 months'
LEFT JOIN project.appointment a ON u.unit_id = a.unit_id
AND a.status = 'Scheduled'
LEFT JOIN project.timeslot ts ON a.timeslot_id = ts.timeslot_id
AND ts.date >= CURRENT_DATE - INTERVAL '3 months'
WHERE u.status = 'Available'
GROUP BY
u.unit_id, u.unit_number, u.price, u.room_number, u.floor_area,
f.floor_number, b.building_id, b.name
)
SELECT
building_name,
unit_number,
floor_number,
room_number,
floor_area,
price,
total_inquiries,
total_appointments,
conversion_rate_pct,
(total_inquiries + total_appointments * 2) AS interest_score,
RANK() OVER (
PARTITION BY building_id
ORDER BY (total_inquiries + total_appointments * 2) DESC
) AS rank_in_building
FROM unit_stats
ORDER BY interest_score DESC, building_name, rank_in_building;
Relational Algebra
Selection of available units and joining to floor and building
AvailableUnits ← σ(status = 'Available')(Unit)
UnitFloorBuilding ← AvailableUnits
⋈(unit.floor_id = floor.floor_id) Floor
⋈(floor.building_id = building.building_id) Building
Selecting recent inquires and scheduled appointments
RecentInquiries ← σ(created_at ≥ CURRENT_DATE − 90)(Inquiry)
RecentScheduledAppts ← σ(appointment.status = 'Scheduled' ∧ timeslot.date ≥ CURRENT_DATE − 90)(
Appointment ⋈(appointment.timeslot_id = timeslot.timeslot_id) Timeslot
)
Left joining units to the inquires and appointments and per unit aggregation
UnitWithActivity ← UnitFloorBuilding
⟕(unit.unit_id = inquiry.unit_id) RecentInquiries
⟕(unit.unit_id = appointment.unit_id) RecentScheduledAppts
UnitStats ← γ(
unit_id, unit_number, price, room_number, floor_area,
floor_number, building_id, name ;
COUNT(inquiry_id) → total_inquiries,
COUNT(appointment_id) → total_appointments,
COUNT(appointment_id) / NULLIF(COUNT(inquiry_id), 0) * 100
→ conversion_rate_pct
)(UnitWithActivity)
Interest score, ranking within building, and final columns
Scored ← ρ(
interest_score ← total_inquiries + total_appointments · 2,
rank_in_building ← RANK() OVER (PARTITION BY building_id
ORDER BY interest_score DESC)
)(UnitStats)
Result ← τ(interest_score DESC, name, rank_in_building)(Scored)
π(name, unit_number, floor_number, room_number, floor_area,
price, total_inquiries, total_appointments,
conversion_rate_pct, interest_score, rank_in_building)(Result)
Client Engagement & Conversion — Semi-Annual
What steps do clients go through over a six month period? This report segments the clients into two types - Cold interest meaning client only made an inquiry, and Warm interest meaning client made an appointment. Ranking them by an engagement score based on the inquiry volume and the appointment count. Showing the average price range for each client and the time taken from first inquiry to an appointment. With the goal for giving insight into the behaviour of clients.
SQL
WITH client_inquiries AS (
SELECT
c.client_id,
c.name AS client_name,
c.email AS client_email,
c.phone AS client_phone,
COUNT(DISTINCT i.inquiry_id) AS total_inquiries,
MIN(i.created_at) AS first_inquiry_date,
MAX(i.created_at) AS last_inquiry_date,
ROUND(AVG(u.price), 2) AS avg_inquired_price
FROM project.client c
JOIN project.inquiry i ON c.client_id = i.client_id
AND i.created_at >= CURRENT_DATE - INTERVAL '6 months'
JOIN project.unit u ON i.unit_id = u.unit_id
GROUP BY c.client_id, c.name, c.email, c.phone
),
client_appointments AS (
SELECT
c.client_id,
COUNT(DISTINCT a.appointment_id) AS total_appointments,
MIN(ts.date) AS first_appointment_date
FROM project.client c
JOIN project.appointment a ON c.client_id = a.client_id
JOIN project.timeslot ts ON a.timeslot_id = ts.timeslot_id
WHERE ts.date >= CURRENT_DATE - INTERVAL '6 months'
GROUP BY c.client_id
)
SELECT
RANK() OVER (
ORDER BY (
ci.total_inquiries +
COALESCE(ca.total_appointments, 0) * 3
) DESC
) AS engagement_rank,
ci.client_name,
ci.client_email,
ci.client_phone,
CASE
WHEN COALESCE(ca.total_appointments, 0) > 0 THEN 'Warm Interest'
ELSE 'Cold Interest'
end AS interest,
ci.total_inquiries,
COALESCE(ca.total_appointments, 0) AS total_appointments,
CASE
WHEN ca.first_appointment_date IS NOT NULL
THEN (ca.first_appointment_date - ci.first_inquiry_date::date)
ELSE NULL
end AS days_to_first_appointment,
ci.avg_inquired_price,
ci.first_inquiry_date,
ci.last_inquiry_date
FROM client_inquiries ci
LEFT JOIN client_appointments ca ON ci.client_id = ca.client_id
ORDER BY engagement_rank;
