Предполагая, что дерево имеет максимальную глубину в четыре уровня, вы можете использовать несколько ЛЕВЫХ СОЕДИНЕНИЙ для получения полного дерева или поддерева.Это не будет супер эффективным, хотя.Рассмотрим следующий запрос:
SET @subtree_id = 1;
SELECT
c0.category_id AS c0_id, c0.name AS c0_name,
c1.category_id AS c1_id, c1.name AS c1_name,
c2.category_id AS c2_id, c2.name AS c2_name,
c3.category_id AS c3_id, c3.name AS c3_name,
l.listing_id
FROM category AS c0
LEFT JOIN category AS c1 ON c1.parent_id = c0.category_id
LEFT JOIN category AS c2 ON c2.parent_id = c1.category_id
LEFT JOIN category AS c3 ON c3.parent_id = c2.category_id
LEFT JOIN listing AS l ON l.category_id = c0.category_id
OR l.category_id = c1.category_id
OR l.category_id = c2.category_id
OR l.category_id = c3.category_id
WHERE c0.category_id = @subtree_id;
Он выдаст следующие результаты:
| c0_id | c0_name | c1_id | c1_name | c2_id | c2_name | c3_id | c3_name | listing_id |
|-------|-------------|-------|-------------|-------|-----------|-------|------------|------------|
| 1 | Real Estate | 2 | Residential | 3 | House | NULL | NULL | NULL |
| 1 | Real Estate | 2 | Residential | 4 | Apartment | NULL | NULL | 1 |
| 1 | Real Estate | 2 | Residential | 4 | Apartment | NULL | NULL | 2 |
| 1 | Real Estate | 2 | Residential | 4 | Apartment | NULL | NULL | 3 |
| 1 | Real Estate | 2 | Residential | 5 | Condo | NULL | NULL | NULL |
| 1 | Real Estate | 6 | Commercial | 7 | Office | NULL | NULL | 4 |
| 1 | Real Estate | 6 | Commercial | 8 | Retail | NULL | NULL | 5 |
| 1 | Real Estate | 6 | Commercial | 9 | Other | 10 | Industrial | 6 |
К сожалению, он содержит только полные пути.Чтобы соответствовать ожидаемому результату, просто разбейте каждую строку на 4 строки:
SET @subtree_id = 1;
SELECT
CASE WHEN level >= 0 THEN c0_id END AS c0_id, CASE WHEN level >= 0 THEN c0_name END AS c0_name,
CASE WHEN level >= 1 THEN c1_id END AS c1_id, CASE WHEN level >= 1 THEN c1_name END AS c1_name,
CASE WHEN level >= 2 THEN c2_id END AS c2_id, CASE WHEN level >= 2 THEN c2_name END AS c2_name,
CASE WHEN level >= 3 THEN c3_id END AS c3_id, CASE WHEN level >= 3 THEN c3_name END AS c3_name,
COUNT(listing_id) AS lc
FROM (
SELECT
c0.category_id AS c0_id, c0.name AS c0_name,
c1.category_id AS c1_id, c1.name AS c1_name,
c2.category_id AS c2_id, c2.name AS c2_name,
c3.category_id AS c3_id, c3.name AS c3_name,
l.listing_id
FROM category AS c0
LEFT JOIN category AS c1 ON c1.parent_id = c0.category_id
LEFT JOIN category AS c2 ON c2.parent_id = c1.category_id
LEFT JOIN category AS c3 ON c3.parent_id = c2.category_id
LEFT JOIN listing AS l ON l.category_id = c0.category_id
OR l.category_id = c1.category_id
OR l.category_id = c2.category_id
OR l.category_id = c3.category_id
WHERE c0.category_id = @subtree_id
) AS paths
INNER JOIN (
SELECT 0 AS level UNION ALL
SELECT 1 UNION ALL
SELECT 2 UNION ALL
SELECT 3
) AS levels ON level = 0 AND c0_id IS NOT NULL
OR level = 1 AND c1_id IS NOT NULL
OR level = 2 AND c2_id IS NOT NULL
OR level = 3 AND c3_id IS NOT NULL
GROUP BY 1, 2, 3, 4, 5, 6, 7, 8
ORDER BY 2, 1, 4, 3, 6, 5, 8, 7
Демонстрация на БД <> Fiddle