how would I optimize this wordpress query? - mysql

I'm trying to figure out the optimal combination of indexes for the query below. Right now its Using temporary; Using filesort and killing my vps. Queries take from 6-10 seconds.
SELECT SQL_CALC_FOUND_ROWS
wp_posts.ID
FROM
wp_posts
INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id)
INNER JOIN wp_term_taxonomy ON (wp_term_relationships.term_taxonomy_id = wp_term_taxonomy.term_taxonomy_id)
INNER JOIN wp_terms ON (wp_term_taxonomy.term_id = wp_terms.term_id)
WHERE
1=1
AND wp_term_taxonomy.taxonomy = 'post_tag'
AND wp_terms.slug IN ('pie')
AND wp_posts.post_type = 'post'
AND (wp_posts.post_status = 'publish')
GROUP BY
wp_posts.ID
ORDER BY
wp_posts.post_date DESC
LIMIT 0, 20
Any suggestions?

How does this compare, performance-wise?
SELECT
p.ID
FROM
wp_posts AS p
WHERE
1=1
AND p.post_type = 'post'
AND p.post_status = 'publish'
AND EXISTS (
SELECT 1
FROM wp_term_relationships AS r
INNER JOIN wp_term_taxonomy AS t ON
r.term_taxonomy_id = t.term_taxonomy_id AND t.taxonomy = 'post_tag'
INNER JOIN wp_terms AS m ON
m.term_id = t.term_id AND m.slug IN ('pie')
WHERE r.object_id = wp_posts.ID
)
ORDER BY
p.post_date DESC
LIMIT 0, 20
Indexes should be on all the primary and foreign keys, on wp_terms.slug, on wp_term_taxonomy.taxonomy and a composite one over wp_posts.post_status, post_type, post_date.

Related

WP: Get wp_post, wp_meta_value[contact] for specific term id

I've been trying to figure out how to get the wp_meta_value depending on the term ID.
This is where I'm at so far but my head is spinning now! If you can help, I'd much appreciate it :)
Thanks
SELECT wp_posts.post_title
FROM wp_posts
LEFT JOIN wp_term_relationships ON (
wp_posts.ID = wp_term_relationships.object_id
)
LEFT JOIN wp_term_taxonomy ON (
wp_term_relationships.term_taxonomy_id = wp_term_taxonomy.term_taxonomy_id
)
WHERE wp_posts.post_status = 'publish'
AND wp_term_taxonomy.term_id =2
ORDER BY post_title DESC
LIMIT 0 , 30
This seems to work...
SELECT wp_posts.post_title, wp_postmeta.meta_value AS 'Contact Details'
FROM wp_posts
LEFT JOIN wp_term_relationships ON ( wp_posts.ID = wp_term_relationships.object_id )
LEFT JOIN wp_term_taxonomy ON ( wp_term_relationships.term_taxonomy_id = wp_term_taxonomy.term_taxonomy_id )
LEFT JOIN wp_postmeta ON ( wp_postmeta.post_id = wp_posts.ID )
WHERE wp_posts.post_status = 'publish'
AND wp_postmeta.meta_key = 'contact'
AND wp_term_taxonomy.term_id =2
ORDER BY post_title;

Ordering results of MySQL query (using join)

In a WordPress installation, I need to order products so that:
Sold products show up last.
Sold products tagged "antique" show up after sold products tagged "reproduction."
I have successfully completed the first item, but I am at a loss regarding the second item. I'm not getting any errors. My problem is that everything in my ORDER BY statement is working except for "wt.slug DESC." If I change the LEFT JOIN statements for wp_term_taxonomy and wp_terms tables to INNER JOIN statements, I get 0 results, so it looks to me like these statements are not finding the results that I expect. Here is my query:
SELECT SQL_CALC_FOUND_ROWS wp_posts.ID
FROM wp_posts
INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id)
INNER JOIN wp_postmeta ON ( wp_posts.ID = wp_postmeta.post_id )
LEFT JOIN wp_postmeta stock ON ( wp_posts.ID = stock.post_id AND stock.meta_key = '_stock_status' )
LEFT JOIN wp_term_taxonomy wtt ON ( wp_term_relationships.term_taxonomy_id = wtt.term_taxonomy_id AND wtt.taxonomy = 'product_tag' )
LEFT JOIN wp_terms wt ON ( wtt.term_id = wt.term_id AND ( wt.slug = 'antique' OR wt.slug = 'reproduction' ) )
WHERE 1=1
AND ( wp_term_relationships.term_taxonomy_id IN (171) )
AND ( ( wp_postmeta.meta_key = '_visibility' AND CAST(wp_postmeta.meta_value AS CHAR) IN ('visible','catalog') ) )
AND wp_posts.post_type = 'product'
AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private')
GROUP BY wp_posts.ID
ORDER BY wp_posts.post_type DESC, stock.meta_value ASC, wt.slug DESC, wp_posts.post_date
Here is the WordPress database description for reference. I would appreciate any assistance.
You can create a new field as
SELECT SQL_CALC_FOUND_ROWS wp_posts.ID, CASE wt.slug WHEN 'antique' THEN -1 WHEN 'reproduction' THEN 0 ELSE 1 END as sort_order
and use the sort_order in ORDER BY clause like
ORDER BY sort_order DESC
Thanks to help from #mynawaz, I have been able to come up with a solution. I'm not sure if it's the most efficient or elegant solution, but it works:
SELECT SQL_CALC_FOUND_ROWS wp_posts.* , CASE wt.slug WHEN 'antique' THEN 1 WHEN 'reproduction' THEN 2 ELSE 0 END as slug_order
FROM wp_posts
INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id)
INNER JOIN wp_postmeta ON ( wp_posts.ID = wp_postmeta.post_id )
LEFT JOIN wp_postmeta stock ON ( wp_posts.ID = stock.post_id AND stock.meta_key = '_stock_status' )
LEFT JOIN wp_term_relationships wtr ON ( wp_posts.ID = wtr.object_id AND stock.meta_value = 'outofstock' )
LEFT JOIN wp_term_taxonomy wtt ON ( wtr.term_taxonomy_id = wtt.term_taxonomy_id AND wtt.taxonomy = 'product_tag' )
LEFT JOIN wp_terms wt ON ( wtt.term_id = wt.term_id AND wt.slug IN( 'antique','reproduction' ) )
WHERE 1=1
AND ( wp_term_relationships.term_taxonomy_id IN (171) )
AND ( ( wp_postmeta.meta_key = '_visibility' AND CAST(wp_postmeta.meta_value AS CHAR) IN ('visible','catalog') ) )
AND wp_posts.post_type = 'product'
AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private')
AND NOT (
stock.meta_value = 'outofstock'
AND ( CASE wt.slug WHEN 'antique' THEN 1 WHEN 'reproduction' THEN 2 ELSE 0 END ) = 0
)
GROUP BY wp_posts.ID, slug_order
ORDER BY wp_posts.post_type DESC, stock.meta_value ASC, slug_order DESC, wp_posts.post_date DESC

Get Wordpress posts that match 2 different categories via custom SQL statement

I'm looking to write a custom SQL statement that will pull published posts from a Wordpress DB that match 2 different categories.
Category 1 (Static) = "Website-1"
Category 2 (Dynamic) = "News", "Tips", "Recreation", etc.
This is a little out of my realm so any help would be greatly appreciated.This is what I have so far:
select p.* from wp_terms wt
join wp_term_taxonomy t on wt.term_id = t.term_id
join wp_term_relationships wpr on wpr.term_taxonomy_id = t.term_taxonomy_id
join wp_posts p on p.id = wpr.object_id
where
t.taxonomy = 'category' and
wt.name = 'Website-1' and
p.post_status = 'publish'
group by p.id
order by p.post_date desc
limit 10
It will pull the first category no problem but I need it to match on 2 categories.
Any insight would be greatly appreciated.
Thanks!
The solution I came up with:
select p.* from wp_posts p
join wp_term_relationships tr on p.id = tr.object_id
join wp_term_taxonomy tt on tt.term_taxonomy_id = tr.term_taxonomy_id
join wp_terms t on t.term_id = tt.term_id
where p.id in
(select tr2.object_id from wp_term_relationships tr2
join wp_term_taxonomy tt2 on tt2.term_taxonomy_id = tr2.term_taxonomy_id
join wp_terms t2 on t2.term_id = tt2.term_id
where
tt2.taxonomy = 'category' and
t2.name in ('Website-1') and
p.id = tr2.object_id
) and
p.post_status = 'publish' and
tt.taxonomy = 'category' and
t.name in ('News')
group by p.id
order by p.post_date desc
limit 10
I'm sure there's a better way to write this query since it's pretty messy but it works for now.
Try having instead of where after group by. I would add this as question / comment but I don't have enough points... what happens if you switch the order of the categories?

MySQL query condition on joined tables

I have the following MySQL Query (querying wordpress db):
SELECT wp_posts.ID, wp_posts.post_date, wp_posts.post_content, wp_posts.post_title, wp_terms.name, wp_term_taxonomy.taxonomy
FROM wp_posts
LEFT JOIN wp_term_relationships ON ( wp_term_relationships.object_id = wp_posts.ID )
LEFT JOIN wp_term_taxonomy ON ( wp_term_taxonomy.term_taxonomy_id = wp_term_relationships.term_taxonomy_id )
LEFT JOIN wp_terms ON ( wp_terms.term_id = wp_term_taxonomy.term_id)
WHERE 1=1
AND wp_terms.name != 'MyTagName'
AND wp_posts.post_type = 'post'
AND (wp_posts.post_status = 'publish')
ORDER BY wp_posts.post_date DESC
LIMIT 100
I am basically trying to STOP all 'wp_posts' coming back that have a tag (db table wp_terms) 'MyTagName'. But all the above seems to do is strip that tag name out from the rows returned, leaving the wp_post entry in there with the other tag entries it is tagged with.
Can anyone help me with this?
SELECT wp_posts.ID, wp_posts.post_date, wp_posts.post_content, wp_posts.post_title, wp_terms.name, wp_term_taxonomy.taxonomy
FROM wp_posts
JOIN wp_term_relationships ON wp_term_relationships.object_id = wp_posts.ID
JOIN wp_term_taxonomy ON wp_term_taxonomy.term_taxonomy_id = wp_term_relationships.term_taxonomy_id
JOIN wp_terms ON wp_terms.term_id = wp_term_taxonomy.term_id
WHERE NOT EXISTS (
SELECT 1
FROM wp_term_relationships
JOIN wp_term_taxonomy ON wp_term_taxonomy.term_taxonomy_id = wp_term_relationships.term_taxonomy_id
JOIN wp_terms ON wp_terms.term_id = wp_term_taxonomy.term_id
WHERE wp_term_relationships.object_id = wp_posts.ID
AND wp_terms.name = 'MyTagName'
)
AND wp_posts.post_type = 'post'
AND wp_posts.post_status = 'publish'
ORDER BY wp_posts.post_date DESC
LIMIT 100
The problem is that your current solution isn't checking all wp_terms that relate to the post, just the specific one. If there were 5 terms, one of which was 'MyTagName', then that single one would be discluded but the other 4 will still be joined.
Now regarding the:
AND wp_posts.post_type = 'post'
AND wp_posts.post_status = 'publish'
You don't need to put them in the big NOT EXISTS check because that post won't be picked up anyway if either of them are false.

MySQL query for wordpress meta data

I need some help with a query that should return posts based on their wp-postratings score (http://wordpress.org/extend/plugins/wp-postratings/).
The user chooses a minimum rating (0 to 5 stars) and a maximum rating (0 to 5 stars) and the query should return the posts that match. I have it working where the user input for both values is above 0 but I can't seem to get my head around the 0 value. Since 0 represents unrated posts - and hence onces that have no ratings meta data - I need to select not only the posts where the rating is no more than the specified max value, but also every post that has no rating meta data.
How can I do this?? Any help will be very much appreciated!
Here's my current query:
SELECT DISTINCT p.*, (t1.meta_value+0.00) AS ratings_average, (t2.meta_value+0.00) AS ratings_users, (t3.meta_value+0.00) AS ratings_score
FROM wp_posts p
INNER JOIN wp_term_relationships tr ON (p.ID = tr.object_id)
INNER JOIN wp_term_taxonomy tt ON (tr.term_taxonomy_id = tt.term_taxonomy_id)
INNER JOIN wp_terms t ON t.term_id = tt.term_id
LEFT JOIN wp_postmeta AS t1 ON t1.post_id = p.ID
LEFT JOIN wp_postmeta AS t2 ON t1.post_id = t2.post_id
LEFT JOIN wp_postmeta AS t3 ON t3.post_id = p.ID
WHERE t1.meta_key = 'ratings_average'
AND t2.meta_key = 'ratings_users'
AND t3.meta_key = 'ratings_score'
AND p.post_date < NOW()
AND p.post_status = 'publish'
AND (tt.taxonomy = 'post_tag' AND tt.term_id = t.term_id AND t.slug = 'liverpool')
AND ( (t1.meta_value+0.00) IS NULL OR (t1.meta_value+0.00) <= $max_stars )
ORDER BY p.post_date DESC
LIMIT 20
I had to do something similar a while back where I was running a cron job to send posts to another application that weren't already registered. The best method I found was to write a query that checked that the ID was NOT IN a query of posts with the meta key.
SELECT $wpdb->posts.ID
FROM $wpdb->posts
WHERE $wpdb->posts.post_status = 'publish'
AND $wpdb->posts.post_type = 'post'
AND $wpdb->posts.ID NOT IN (
SELECT $wpdb->posts.ID
FROM $wpdb->posts
left join $wpdb->postmeta ON ($wpdb->posts.ID = $wpdb->postmeta.post_id)
WHERE $wpdb->posts.post_status = 'publish'
AND $wpdb->postmeta.meta_key = 'meta_key')
I believe this should work, though I obviously haven't tested it.
SELECT DISTINCT p.*, (t1.meta_value+0.00) AS ratings_average, (t2.meta_value+0.00) AS ratings_users, (t3.meta_value+0.00) AS ratings_score
FROM wp_posts p
INNER JOIN wp_term_relationships tr ON (p.ID = tr.object_id)
INNER JOIN wp_term_taxonomy tt ON (tr.term_taxonomy_id = tt.term_taxonomy_id)
INNER JOIN wp_terms t ON t.term_id = tt.term_id
LEFT JOIN wp_postmeta AS t1 ON t1.post_id = p.ID
LEFT JOIN wp_postmeta AS t2 ON t1.post_id = t2.post_id
LEFT JOIN wp_postmeta AS t3 ON t3.post_id = p.ID
WHERE t1.meta_key = 'ratings_average'
AND t2.meta_key = 'ratings_users'
AND t3.meta_key = 'ratings_score'
AND p.post_date < NOW()
AND p.post_status = 'publish'
AND (tt.taxonomy = 'post_tag'
AND tt.term_id = t.term_id
AND t.slug = 'liverpool')
AND (
p.ID NOT IN (
SELECT p.ID
FROM wp_posts AS p
LEFT JOIN wp_postmeta AS pm ON (pm.post_id = p.ID)
WHERE pm.meta_key = 'ratings_score'
)
OR
(t1.meta_value+0.00) <= $max_stars )
ORDER BY p.post_date DESC
LIMIT 20
Okay, this query seems to work for me. Its a bit ugly though and not too quick so if anyone has a better one feel free to improve upon it!
It selects all of the rated posts that are below the $max_stars value, then combines the table with a separate select which gets all of the non-rated posts:
(SELECT DISTINCT p.*, (t1.meta_value+0.00) AS ratings_average, (t2.meta_value+0.00) AS ratings_users, (t3.meta_value+0.00) AS ratings_score
FROM wp_posts p
INNER JOIN wp_term_relationships tr ON (p.ID = tr.object_id)
INNER JOIN wp_term_taxonomy tt ON (tr.term_taxonomy_id = tt.term_taxonomy_id)
INNER JOIN wp_terms t ON t.term_id = tt.term_id
LEFT JOIN wp_postmeta AS t1 ON t1.post_id = p.ID
LEFT JOIN wp_postmeta AS t2 ON t2.post_id = p.ID
LEFT JOIN wp_postmeta AS t3 ON t3.post_id = p.ID
WHERE t1.meta_key = 'ratings_average'
AND t2.meta_key = 'ratings_users'
AND t3.meta_key = 'ratings_score'
AND p.post_date < NOW()
AND p.post_status = 'publish'
AND (tt.taxonomy = 'post_tag' AND tt.term_id = t.term_id AND t.slug = 'liverpool')
AND (t1.meta_value+0.00) <= $max_stars )
UNION
(SELECT DISTINCT p.*, NULL AS ratings_average, NULL AS ratings_users, NULL AS ratings_score
FROM wp_posts p
INNER JOIN wp_term_relationships tr ON (p.ID = tr.object_id)
INNER JOIN wp_term_taxonomy tt ON (tr.term_taxonomy_id = tt.term_taxonomy_id)
INNER JOIN wp_terms t ON t.term_id = tt.term_id
LEFT JOIN wp_postmeta AS t1 ON (t1.post_id = p.ID AND t1.meta_key = 'ratings_score')
WHERE t1.post_id is null
AND p.post_date < NOW()
AND p.post_status = 'publish'
AND (tt.taxonomy = 'post_tag' AND tt.term_id = t.term_id AND t.slug = 'liverpool') )
ORDER BY post_date DESC