CREATE TABLE test (
id INT PRIMARY KEY,
data TEXT CHECK (JSON_VALID(data)),
INDEX idx_data (data(100)) -- подогнать до разумного
);
SELECT c.name category_name, p.id post_id, p.name post_name, p.title post_title
FROM posts p
JOIN categories c ON p.category_id = c.id
UNION ALL
SELECT name, NULL, NULL, NULL
FROM categories
ORDER BY category_name, post_id IS NOT NULL
SELECT * FROM Categories LEFT JOIN Posts ON Posts.category_id = Categories.id ORDER BY Categories.id ASC
Пока на ум приходит получить все категории, потом в цикле пройтись по ним и...