For MySQL, this query will return the result set you specified:
SELECT b.title
, MAX(b.timestamp)
, b.category_id
FROM blogs b
GROUP BY b.category_id
If there happen to be more than one "title" in a category that has the same latest "timestamp", only one row for that category will be returned, so you will get just one "title" returned for each category.
Note: other DBMS system will throw an exception (error) with a query like the one above, because of the handling of non-aggregates in the SELECT list that don't appear in the GROUP BY.
Your query was very close. You've already got that inner query returning the "latest" timestamp for each category. The next step is to return the category_id along with that latest timestamp.
SELECT category_id, MAX(timestamp) AS timestamp
FROM blogs
GROUP BY category_id
The next step is to join that back to blogs
, to get the associated "title"(s)
SELECT b.title
, b.timestamp
, b.category_id
FROM (SELECT category_id, MAX(timestamp) AS timestamp
FROM blogs
GROUP BY category_id
) l
JOIN blogs b
ON b.category_id = l.category_id AND b.timestamp = l.timestamp
NOTE: if there is more than one "latest" row for a category (the timestamp values match), this query will return both of them.
If that's a concern, the query can be modified (or written in a different way) to prevent any possibility of two rows for a category.
Simply adding a GROUP BY clause to that query will work (in MySQL only, not other DBMSs)
SELECT b.title
, b.timestamp
, b.category_id
FROM (SELECT category_id, MAX(timestamp) AS timestamp
FROM blogs
GROUP BY category_id
) l
JOIN blogs b
ON b.category_id = l.category_id AND b.timestamp = l.timestamp
GROUP BY b.timestamp, b.category_id
(For other DBMSs, you could modify the SELECT list, replace b.title
with MAX(b.title) AS title
. That will work when you are returning a single column from the row.
If you want the rows returned in a particular order, add an ORDER BY clause.