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
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;
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
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?
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.
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