Firstly I would switch to ANSI 92 explicit join syntax rather than the ANSI 89 implicit join syntax you are using, as the name suggests this is over 20 years out of date:
select ...
from cat_products p
INNER JOIN cat_product_catalog_map pcm
ON pcm.product_id=p.product_id
INNER JOIN cat_current_product_prices cpp
ON cpp.product_id = p.product_id
INNER JOIN cat_product_text pt
ON pt.product_id=p.product_id
WHERE ....
This won't affect performance but will make your query more legible, and less prone to accidental cross joins. Aaron Bertrand has written a good article on the reasons to switch that is worth a read (it is aimed at SQL Server but many of the principles are universal). Then I would remove the NOT IN (Subquery)
MySQL does not optimise subqueries like this well. It will rewrite it to:
AND NOT EXISTS (SELECT 1
FROM cat_product_detail_map
WHERE short_value in ('ft_section','ft_product')
AND cat_product_detail_map.product_id = p.product_id
)
It will then execute this subquery once for every row. The inverse of this scenario (WHERE <expression> IN (Subquery)
is described in the article Optimizing Subqueries with EXISTS Strategy)
You can exclude these product_ids using the LEFT JOIN/IS NULL
method which performs better in MySQL as it avoids a subquery completely:
SELECT ...
FROM cat_products p
LEFT JOIN cat_product_detail_map exc
ON exc.product_id = p.product_id
AND exc.short_value in ('ft_section','ft_product')
WHERE exc.product_id IS NULL
This allows for better use of indexes and means that you don't have to execute a subquery for every row in the outer query.
So your full query would then be:
SELECT p.*,
pt.pretty_name,
pt.seo_name,
pt.description,
pt.short_description,
pt.short_description_2
FROM cat_products p
INNER JOIN cat_product_catalog_map pcm
ON pcm.product_id = p.product_id
INNER JOIN cat_current_product_prices cpp
ON cpp.product_id = p.product_id
INNER JOIN cat_product_text pt
ON pt.product_id = p.product_id
LEFT JOIN cat_product_detail_map exc
ON exc.product_id = p.product_id
AND exc.short_value in ('ft_section','ft_product')
WHERE exc.product_id IS NULL
AND pcm.catalog_id = 2
AND p.owner_catalog_id = 2
AND cpp.currency = 'GBP'
AND cpp.catalog_id = 2
AND cpp.state <> 'unavail'
AND pt.language_id = 'EN'
ORDER BY pt.pretty_name limit 200,200;
The final thing to look at would be the indexes on your tables, I don't know what you already have but I'd suggest an index on product_id on each of your tables as a bare minimum, and perhaps on the columns you are filtering on.