I have a site with several custom taxonomies, and have found that one of the slowest parts of the site is attempting to query with an OR across several of these at once. I'm using a WP_Query
like so:
array(
'tax_query' => array(
'relation' => 'OR',
array('taxonomy' => 'tax1', 'field' => 'slug', 'terms' => 'term1'),
array('taxonomy' => 'tax2', 'field' => 'slug', 'terms' => 'term2'),
array('taxonomy' => 'tax3', 'field' => 'slug', 'terms' => 'term3'),
array('taxonomy' => 'tax4', 'field' => 'slug', 'terms' => 'term4'),
)
)
The SQL it generates takes an unacceptable 6 seconds to run:
SELECT SQL_CALC_FOUND_ROWS wp_posts.* FROM wp_posts
INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id)
INNER JOIN wp_term_relationships AS tt1 ON (wp_posts.ID = tt1.object_id)
INNER JOIN wp_term_relationships AS tt2 ON (wp_posts.ID = tt2.object_id)
INNER JOIN wp_term_relationships AS tt3 ON (wp_posts.ID = tt3.object_id)
WHERE 1=1 AND wp_posts.ID NOT IN (70)
AND (wp_term_relationships.term_taxonomy_id IN (23)
OR tt1.term_taxonomy_id IN (5)
OR tt2.term_taxonomy_id IN (11)
OR tt3.term_taxonomy_id IN (10) )
AND (wp_posts.post_status = 'publish')
GROUP BY wp_posts.ID ORDER BY wp_posts.post_date DESC LIMIT 0, 500
But this equivalent query takes a much nicer 0.29 seconds:
SELECT SQL_CALC_FOUND_ROWS wp_posts.* FROM wp_posts
INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id)
WHERE 1=1 AND wp_posts.ID NOT IN (70)
AND (wp_term_relationships.term_taxonomy_id IN (23, 5, 11, 10))
AND (wp_posts.post_status = 'publish')
GROUP BY wp_posts.ID ORDER BY wp_posts.post_date DESC LIMIT 0, 500
Clearly the multiple joins are making it slower than it needs to be. The SQL doesn't care that the terms come from different taxonomies, but WP_Query
does because they're looked up by slug. Is there any way to persuade WP_Query
to generate something closer to the second one?
(Note the above has been anonymised to protect my client)