There are two tables in my project: posts
and post_categories
.
Each post in the posts
table has a post_category_id
relating to post_categories.id
as fk, and each entry in post_categories
has a parent_id
– self-referencing post_categories.id
.
On the website, the post categories page shows all the posts in each category, but I need it to also count all the posts in each subcategory thereof.
post_categories
(0 = main menu)
+-----+---------------+-----------+
| id | title | parent_id |
+-----+---------------+-----------+
| 1 | Machines | 0 |
| 2 | Vehicles | 1 |
| 3 | Industrial | 1 |
| 4 | Road Cars | 2 |
| 5 | Track Cars | 2 |
+-----+---------------+-----------+
posts
+-----+--------+------------------+
| id | title | post_category_id |
+-----+--------+------------------+
| 1 | Rally | 5 |
| 2 | F1 | 5 |
| 3 | Vans | 4 |
| 4 | Trucks | 4 |
+-----+--------+------------------+
So far, if there is a category that does not have any posts directly under it, it shows 0 posts, even if the category contains 2 more categories with a total of 50 posts.
I have been spending a lot of time going round in circles with MySQL RECURSIVE
functions, to no avail. It seems elusively simple, but I can’t find an example of a RECURSIVE
query that takes a second table into consideration and it’s impossible to code for a number of layers, as there is no hard limit to how many levels there could be.
To clarify, then, let’s assume we are at the Vehicles menu (post_category_id: 2).
My query should:
- Count all posts having post_category_id = 2 (none in this case).
- Count all posts having post_category_id IN (4,5) – because post_categories 4 and 5 both have the parent_id = 2.
- Add up those numbers and give my two columns containing the total number of posts and the number of categories, so I can print:
Post Category Title (Contains 30 posts in 6 categories)
Could anyone provide a recursive solution?
2
Answers
I have a couple questions above that will help refine this answer if it is not what you are looking for.
I think this is what you are looking for? If you need something more recursive, then you need to expand your question and give a recursive example. By that I mean a situation where the method would call itself and break out of an infinite loop with a
return
clause.This is probably not the most efficient way but it works. The recursive CTE builds a list of all descendants of the given category (remove the
WHERE
clause from the first query in the CTE to build for all categories):For the sample data provided in your question, it outputs:
If running for all categories you should pre-aggregate the category counts as they will be required multiple times:
which outputs:
Here’s a db<>fiddle