La risposta di Jason è azzeccata. Inoltre proverei a utilizzare la più moderna sintassi di join ANSI per alleggerire il carico della clausola WHERE, quindi allevia la confusione lì:
SELECT o.id
FROM programs o
JOIN titles_programs t ON t.object_id=o.id
JOIN descriptions_programs d ON d.object_id=o.id
WHERE MATCH (d.text) AGAINST ('+china' IN BOOLEAN MODE) AND d.current=1
OR MATCH (t.text) AGAINST ('+china' IN BOOLEAN MODE) AND t.current=1
Ciò fermerà l'involontaria unione incrociata causando un'esplosione combinatoria; Mi aspetto che funzioni in tempi ragionevoli a meno che il database non sia davvero enorme.
In caso negativo, puoi pubblicare i risultati di un EXPLAIN SELECT di quanto sopra? Presumibilmente uno o entrambi gli indici fulltext non vengono utilizzati. Potrei certamente immaginare che l'ottimizzatore di query non riesca a utilizzare il secondo indice fulltext, facendo qualcosa come provare a "riempire" le righe che non corrispondevano alla prima query fulltext invece di andare direttamente all'indice o qualcosa del genere.
Normalmente quando si desidera indicizzare il testo completo su due colonne in combinazione, si crea un indice su entrambe le colonne. Questo sarebbe in ogni caso molto più veloce. Tuttavia significherebbe che devi inserire titoli e descrizioni nella stessa tabella. Potrebbe non essere una tale difficoltà:poiché il fulltext funziona solo su tabelle MyISAM (e in genere non vuoi i tuoi dati canonici nelle tabelle MyISAM) puoi conservare la copia definitiva dei tuoi dati in tabelle InnoDB correttamente normalizzate, con una tabella MyISAM aggiuntiva contenente solo esche da ricerca spogliate e con gambo.
Se nulla di tutto ciò va bene... beh, suppongo che tornerei all'UNIONing che hai menzionato, insieme a un filtro a livello di applicazione per rimuovere gli ID duplicati.