✏️ Explanatory Question

Trips and Users — calculate the daily cancellation rate

👁 11 Views
📘 Detailed Answer
🟢 Easy
💡

Answer with Explanation

153

Trips and Users — calculate the daily cancellation rate

Level: Coding Round — A LeetCode HARD problem; combines multi-table joins, conditional aggregation, and filtering out banned users.

The Puzzle: Find the daily cancellation rate of trips where neither the rider nor the driver is banned. The cancellation rate for a day = (cancelled unbanned trips) / (total unbanned trips), rounded to 2 decimals.

Sample Data

trips

idclient_iddriver_idstatusrequest_at
1110completed2026-08-01
2211cancelled_by_driver2026-08-01
3312completed2026-08-01
4413cancelled_by_client2026-08-01

users

idbannedrole
1Noclient
4Yesclient
10Nodriver

Trip 4's client (id 4) is banned, so trip 4 is excluded entirely from both numerator and denominator.

Expected Output

DayCancellation Rate
2026-08-010.33

After removing banned trip 4: 3 valid trips, 1 cancelled (trip 2). Rate = 1/3 = 0.33.

The three-part strategy: (1) Exclude banned users — both the client AND the driver must be unbanned. (2) Count cancellations with conditional aggregation (SUM of a CASE). (3) Divide cancelled by total, per day, rounded to 2 decimals.

Solution — Join, Filter Banned, Conditional Aggregate

SELECT
    t.request_at AS 'Day',
    ROUND(
        SUM(CASE WHEN t.status LIKE 'cancelled%' THEN 1 ELSE 0 END)
        / COUNT(*),
    2) AS 'Cancellation Rate'
FROM trips t
JOIN users c ON t.client_id = c.id AND c.banned = 'No'   -- unbanned client
JOIN users d ON t.driver_id = d.id AND d.banned = 'No'   -- unbanned driver
GROUP BY t.request_at
ORDER BY t.request_at;

The key trap — banned filter in the JOIN: By putting c.banned = 'No' and d.banned = 'No' in the JOIN conditions, any trip with a banned client OR driver is dropped from both the numerator and denominator — exactly what the problem requires. If you filtered only cancellations by banned status, the denominator would be wrong.

Alternative — Using AVG with a Boolean

-- A boolean condition is 1/0, so AVG() directly gives the rate
SELECT
    t.request_at AS 'Day',
    ROUND(AVG(t.status LIKE 'cancelled%'), 2) AS 'Cancellation Rate'
FROM trips t
JOIN users c ON t.client_id = c.id AND c.banned = 'No'
JOIN users d ON t.driver_id = d.id AND d.banned = 'No'
GROUP BY t.request_at;
-- AVG of (1s and 0s) = fraction that are cancelled = the rate

The elegant AVG trick: In MySQL, a comparison like status LIKE 'cancelled%' evaluates to 1 or 0. Averaging those values gives the proportion that are true — i.e., the cancellation rate directly, no explicit division needed. This is a clean, common shortcut for "what fraction meets a condition."

Interviewer follow-up: "The real table has millions of trips across years — how do you keep this fast?" → Add an index on trips(request_at) for the grouping, ensure users(id, banned) supports the join filter, and constrain the date range in a WHERE (e.g., a specific month) so you do not scan all history. For recurring dashboards, pre-aggregate daily rates into a summary table rather than recomputing over raw trips each time.