Aggiungi una colonna a Categorie che fornisce la categoria principale in cui si trova ciascuna categoria (con le categorie principali che si danno da sole). Quindi:
cat_id | main_cat_id | title
-------+-------------+---------
01 | 01 | Science
0101 | 01 | Medicine
02 | 02 | Sport
Seleziona da questo su cat_id =main_cat_id per trovare le categorie principali; unisciti di nuovo a se stesso su left.cat_id =right.main_cat_id per trovare le categorie figlio, quindi sui post su cat_id =cat_id. Raggruppa per left.cat_id e proietta su cat_id e count(*).
L'ho provato in PostgreSQL 8.4 e non vedo perché questo non funzionerebbe in MySQL, poiché la query è piuttosto semplice. I miei tavoli:
create table categories(
cat_id varchar(40) primary key,
main_cat_id varchar(40) not null references categories,
title varchar(40) not null
)
create table posts (
post_id integer primary key,
cat_id varchar(40) not null references categories,
title varchar(40) not null
)
La mia query (raggruppamento per titolo anziché per ID):
select m.title, count(*)
from categories m, categories c, posts p
where m.cat_id = c.main_cat_id
and c.cat_id = p.cat_id
group by m.title
AGGIORNAMENTO:ho anche avuto la possibilità di farlo funzionare con un'operazione di stringa, come ha provato l'OP. La query (in SQL conforme agli standard come accettato da PostgreSQL, piuttosto che nel dialetto di MySQL) è:
select m.title, count(*)
from categories m, posts p
where m.cat_id = substring(p.cat_id from 1 for 2)
group by m.title;
Che funziona bene. Non posso offrire un confronto significativo per quanto riguarda la velocità, ma il piano di query per questo sembrava un po' più semplice di quello per l'unione a due vie.