La respuesta de Jason es acertada. Además, trataría de usar la sintaxis de unión ANSI más moderna para aliviar la carga de la cláusula WHERE para aliviar la confusión allí:
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
Esto evitará que la unión cruzada inadvertida provoque una explosión combinatoria; Espero que funcione en un tiempo razonable a menos que la base de datos sea realmente enorme.
Si no, ¿puede publicar los resultados de EXPLICAR SELECCIÓN de lo anterior? Presumiblemente, uno o ambos índices de texto completo no se están utilizando. Ciertamente podría imaginar que el optimizador de consultas no pudiera usar el segundo índice de texto completo, haciendo algo como intentar 'rellenar' las filas que no coincidían con la primera consulta de texto completo en lugar de ir directamente al índice, o algo así.
Normalmente, cuando desea indexar el texto completo en dos columnas combinadas, crea un índice en ambas columnas. En cualquier caso, esto sería mucho más rápido. Sin embargo, significaría que tiene que poner títulos y descripciones en la misma tabla. Puede que esto no sea una dificultad:dado que el texto completo solo funciona en tablas MyISAM (y normalmente no desea que sus datos canónicos estén en tablas MyISAM), puede conservar la copia definitiva de sus datos en tablas InnoDB correctamente normalizadas, con una tabla MyISAM adicional. que contiene solo cebo de búsqueda pelado y sin tallo.
Si nada de eso es bueno... bueno, supongo que volvería a UNIONing que mencionaste, junto con un filtro a nivel de aplicación para eliminar ID duplicados.