I have a database of items. Each item is categorized with a category ID from a category table. I am trying to create a page that lists every category, and underneath each category I want to show the 4 newest items in that category.
For Example:
Pet Supplies
img1
img2
img3
img4
Pet Food
img1
img2
img3
img4
I know that I could easily solve this problem by querying the database for each category like so:
SELECT id FROM category
Then iterating over that data and querying the database for each category to grab the newest items:
SELECT image FROM item where category_id = :category_id ORDER BY date_listed DESC LIMIT 4
What I’m trying to figure out is if I can just use 1 query and grab all of that data. I have 33 categories so I thought perhaps it would help reduce the number of calls to the database.
Anyone know if this is possible? Or if 33 calls isn’t that big a deal and I should just do it the easy way.
This is the greatest-n-per-group problem, and it’s a very common SQL question.
Here’s how I solve it with outer joins:
I’m assuming the primary key of the
itemtable isitem_id, and that it’s a monotonically increasing pseudokey. That is, a greater value initem_idcorresponds to a newer row initem.Here’s how it works: for each item, there are some number of other items that are newer. For example, there are three items newer than the fourth newest item. There are zero items newer than the very newest item. So we want to compare each item (
i1) to the set of items (i2) that are newer and have the same category asi1. If the number of those newer items is less than four,i1is one of those we include. Otherwise, don’t include it.The beauty of this solution is that it works no matter how many categories you have, and continues working if you change the categories. It also works even if the number of items in some categories is fewer than four.
Another solution that works but relies on the MySQL user-variables feature:
MySQL 8.0.3 introduced support for SQL standard window functions. Now we can solve this sort of problem the way other RDBMS do: