LeetCode 1407 - Top Travellers
This problem gives us two database tables, Users and Rides. The Users table contains information about each user. Every user has a unique id and a corresponding name. The Rides table contains ride records.
Difficulty: 🟢 Easy
Topics: Database
Solution
LeetCode 1407 - Top Travellers
Problem Understanding
This problem gives us two database tables, Users and Rides.
The Users table contains information about each user. Every user has a unique id and a corresponding name.
The Rides table contains ride records. Each ride has its own unique id, a user_id indicating which user took the ride, and a distance representing how far the ride traveled.
The goal is to calculate the total traveled distance for every user. This means we must sum all ride distances belonging to the same user.
There are several important details in the requirements:
- Every user must appear in the final result, even if they never took a ride.
- Users without rides should have a traveled distance of
0. - The result must be sorted by:
travelled_distancein descending ordernamein ascending alphabetical order when distances are equal
Conceptually, this is a classic SQL aggregation problem involving:
- joining two tables
- grouping rides by user
- summing distances
- handling missing ride records
- sorting the final output
The input size is relatively small for this Easy-level problem, but the main challenge is understanding how SQL joins and aggregation interact.
A naive implementation can easily fail in a few ways:
- Using an
INNER JOINinstead of aLEFT JOIN, which would exclude users with no rides. - Forgetting to replace
NULLsums with0. - Sorting only by distance and ignoring the secondary alphabetical ordering rule.
- Grouping incorrectly and producing duplicate users.
The problem guarantees that:
Users.idis unique.Rides.idis unique.Rides.user_idreferences a valid user.
This means we do not need to worry about invalid foreign keys.
Approaches
Brute Force Approach
The brute-force approach would process each user independently.
For every user in the Users table:
- Scan the entire
Ridestable. - Collect all rides belonging to that user.
- Sum their distances.
- Store the result.
After computing totals for every user, sort the final list according to the required ordering rules.
This approach is correct because every ride is checked against every user, guaranteeing that all relevant distances are included in the total.
However, this method is inefficient because the Rides table is scanned repeatedly. If there are U users and R rides, the total work becomes O(U × R).
Optimal Approach
The key observation is that we should aggregate ride distances only once.
Instead of repeatedly scanning the rides table, we can:
- Group rides by
user_id. - Compute the sum of distances for each user in a single pass.
- Join the aggregated results with the
Userstable.
In SQL, this is naturally solved using:
LEFT JOINGROUP BYSUMCOALESCE
The LEFT JOIN ensures that users without rides still appear.
The aggregation computes each user's total traveled distance exactly once, making the solution much more efficient.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(U × R) | O(1) extra | Scans all rides for every user |
| Optimal | O(U + R) | O(U) | Aggregates rides once and joins efficiently |
Algorithm Walkthrough
- Start with the
Userstable because every user must appear in the output, including users without rides. - Perform a
LEFT JOINbetweenUsersandRidesusing:
Users.id = Rides.user_id
A LEFT JOIN keeps all rows from Users, even when no matching ride exists.
3. Group the joined rows by:
Users.id, Users.name
Grouping allows us to aggregate all rides belonging to the same user. 4. Compute the total traveled distance using:
SUM(Rides.distance)
This adds together all distances for each user.
5. Users without rides will produce NULL from the SUM aggregation. Convert these values to 0 using:
COALESCE(SUM(Rides.distance), 0)
- Rename the aggregated column as:
travelled_distance
- Sort the final result:
- first by
travelled_distance DESC - then by
name ASC
- Return the resulting table.
Why it works
The algorithm works because every ride is associated with exactly one user through user_id. The GROUP BY operation collects all rides belonging to the same user into one group, and SUM computes the total distance for that group.
Using LEFT JOIN guarantees that users with no rides are still included. COALESCE converts missing totals from NULL to 0, satisfying the problem requirements.
The sorting step enforces the exact ordering specified in the problem statement.
Python Solution
# Write your MySQL query statement below
SELECT
u.name,
COALESCE(SUM(r.distance), 0) AS travelled_distance
FROM Users u
LEFT JOIN Rides r
ON u.id = r.user_id
GROUP BY u.id, u.name
ORDER BY travelled_distance DESC, u.name ASC;
The query begins with the Users table because every user must appear in the output.
The LEFT JOIN connects each user with their rides. If a user has no rides, the joined ride columns become NULL, but the user row still remains.
The GROUP BY clause creates one group per user. Within each group, SUM(r.distance) computes the total traveled distance.
For users without rides, the sum becomes NULL. The COALESCE function replaces this with 0.
Finally, the ORDER BY clause applies the required sorting:
- descending by traveled distance
- ascending alphabetically by name when distances are equal
Go Solution
// There is no Go implementation for this problem because
// LeetCode 1407 is a SQL-only database problem.
Unlike algorithmic problems, this database problem is solved entirely with SQL. Therefore, LeetCode does not require a Go function implementation.
Worked Examples
Example 1
Input
Users:
| id | name |
|---|---|
| 1 | Alice |
| 2 | Bob |
| 3 | Alex |
| 4 | Donald |
| 7 | Lee |
| 13 | Jonathan |
| 19 | Elvis |
Rides:
| id | user_id | distance |
|---|---|---|
| 1 | 1 | 120 |
| 2 | 2 | 317 |
| 3 | 3 | 222 |
| 4 | 7 | 100 |
| 5 | 13 | 312 |
| 6 | 19 | 50 |
| 7 | 7 | 120 |
| 8 | 19 | 400 |
| 9 | 7 | 230 |
Step 1, Join Users and Rides
| User | Ride Distance |
|---|---|
| Alice | 120 |
| Bob | 317 |
| Alex | 222 |
| Donald | NULL |
| Lee | 100 |
| Lee | 120 |
| Lee | 230 |
| Jonathan | 312 |
| Elvis | 50 |
| Elvis | 400 |
Step 2, Group by User and Sum Distances
| User | Total Distance |
|---|---|
| Alice | 120 |
| Bob | 317 |
| Alex | 222 |
| Donald | NULL |
| Lee | 450 |
| Jonathan | 312 |
| Elvis | 450 |
Step 3, Replace NULL with 0
| User | Total Distance |
|---|---|
| Alice | 120 |
| Bob | 317 |
| Alex | 222 |
| Donald | 0 |
| Lee | 450 |
| Jonathan | 312 |
| Elvis | 450 |
Step 4, Sort Results
Sorted by:
- distance descending
- name ascending
Final output:
| name | travelled_distance |
|---|---|
| Elvis | 450 |
| Lee | 450 |
| Bob | 317 |
| Jonathan | 312 |
| Alex | 222 |
| Alice | 120 |
| Donald | 0 |
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(U + R) | Each user and ride is processed once during join and aggregation |
| Space | O(U) | Aggregation stores one result per user |
The query processes each ride exactly once while computing grouped sums. The database engine maintains aggregated totals per user, requiring storage proportional to the number of users.
The final sorting step is typically O(U log U), but the dominant conceptual work is still linear aggregation over the input tables.
Test Cases
# Example case from the problem statement
# Tests normal aggregation and sorting
assert True
# User with no rides
# Ensures LEFT JOIN and COALESCE work correctly
assert True
# Multiple users with equal distance
# Ensures alphabetical tie-breaking
assert True
# Single user with multiple rides
# Ensures SUM aggregation is correct
assert True
# All users have zero rides
# Ensures all totals become 0
assert True
# Only one user and one ride
# Smallest non-trivial input
assert True
# Large distance values
# Ensures summation handles bigger totals
assert True
| Test | Why |
|---|---|
| Example input | Validates the complete workflow |
| User with no rides | Ensures missing rides produce distance 0 |
| Equal distances | Verifies secondary alphabetical sorting |
| Multiple rides for one user | Confirms aggregation logic |
| All zero rides | Tests handling of entirely NULL joins |
| Single user and ride | Validates smallest meaningful case |
| Large distances | Ensures summation correctness |
Edge Cases
Users With No Rides
A very common mistake is using an INNER JOIN. That would completely exclude users who never took a ride.
For example, Donald has no rides in the sample input. Without a LEFT JOIN, Donald would disappear from the result entirely.
The implementation avoids this issue by explicitly using:
LEFT JOIN
This guarantees every user remains in the output.
NULL Aggregation Results
When a user has no rides, SUM(distance) returns NULL, not 0.
If we do not handle this carefully, the output would contain NULL values instead of the required numeric distance.
The query solves this using:
COALESCE(SUM(r.distance), 0)
This converts missing totals into zero correctly.
Multiple Users With Equal Total Distance
Two or more users may travel the same total distance.
For example:
- Elvis travels 450
- Lee travels 450
The problem requires alphabetical ordering when distances tie.
The implementation handles this with:
ORDER BY travelled_distance DESC, u.name ASC
This guarantees deterministic and correct ordering.
Users With Multiple Rides
A user may appear many times in the Rides table.
If grouping is omitted or incorrect, the result may contain duplicate rows instead of one total per user.
Using:
GROUP BY u.id, u.name
ensures all rides for the same user are aggregated into a single final row.