I’m making a web app that uses geolocation, and I’m whipping up a “nearby places” view. It’s pretty simple logic: it shows the closest 5 places that are stored in the database. Perfect.
The trick is that I want it to return the closest 5 places OR all of the places within two miles, whichever is greater. In other words, I want the user to be able to see at least all the places within two miles, but if there aren’t 5 places in within that radius, I want them to show the nearest 5.
Let’s use these for sample data sets. Set 1:
| id | name | distance |
+----+----------------------+-----------+
| 3 | Earl of Sandwich | 0.3 |
| 4 | Nails 'n More | 0.8 |
| 22 | City Hotel | 1.7 |
| 5 | Mighty Medicine | 2.1 |
| 25 | Wonder Wings | 2.5 |
| 6 | Jean Warehouse | 2.7 |
| 9 | Ship Safe & Speedy | 2.9 |
| 2 | Bagel Bonus | 4.1 |
+----+----------------------+-----------+
Set 2:
| id | name | distance |
+----+----------------------+-----------+
| 3 | Earl of Sandwich | 0.1 |
| 4 | Nails 'n More | 0.2 |
| 5 | Mighty Medicine | 0.5 |
| 6 | Jean Warehouse | 0.7 |
| 9 | Ship Safe & Speedy | 0.9 |
| 2 | Bagel Bonus | 1.2 |
| 22 | City Hotel | 1.7 |
| 25 | Wonder Wings | 2.1 |
+----+----------------------+-----------+
In the first set, I’d want to return rows 3, 4, 22, 5, and 25. In the second set, I’d want to show 3, 4, 5, 6, 9, 2, and 22.
I know I could just always limit the query to, say, 100 places, and go through the result set in PHP to filter… but I’m wondering if there’s a more efficient way to do it right in SQL.
In summary, the way to do this is to run both the queries and take a UNION of the sets. That’s it. There really is very little performance loss doing this because the first set (that can produce >5 rows) is already required if there really are more than 5 rows in the result.
In detail – original answer below
For illustration, instead of using 2 data sets, I just use 2 columns in this same sample table, for which the query is shown further below:
And the queries and results:
The performance of this query is as good as it gets.
The first UNION part picks out the ones <2km. This is necessary since you want all the matches.
The next part selects the top 5 and assuming you have an index, this is trivial to collect.
The combination (UNION) of both parts very quick.