MySQL query for wordpress meta data - mysql

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

Related

MySQL Query Retrieve Tags in TWO Categories (AND not OR) [WordPress Raw SQL]

Struggling with this for a while
SELECT DISTINCT terms2.term_id as tag_id, terms2.name as tag_name, terms2.slug as tag_slug, t2.description as tag_desc
FROM
wp_posts as p1
LEFT JOIN wp_term_relationships as r1 ON p1.ID = r1.object_ID
LEFT JOIN wp_term_taxonomy as t1 ON r1.term_taxonomy_id = t1.term_taxonomy_id
LEFT JOIN wp_terms as terms1 ON t1.term_id = terms1.term_id,
wp_posts as p2
LEFT JOIN wp_term_relationships as r2 ON p2.ID = r2.object_ID
LEFT JOIN wp_term_taxonomy as t2 ON r2.term_taxonomy_id = t2.term_taxonomy_id
LEFT JOIN wp_terms as terms2 ON t2.term_id = terms2.term_id
WHERE
t1.taxonomy = 'product_cat' AND p1.post_status = 'publish' AND terms1.term_id IN (224,229) AND
t2.taxonomy = 'product_tag' AND p2.post_status = 'publish'
AND p1.ID = p2.ID
ORDER by tag_name
Line I am dealing with is this one:
t1.taxonomy = 'product_cat' AND p1.post_status = 'publish' AND terms1.term_id IN (224,229) AND
This works, but the IN clause acts as an OR--- so it is bringing back tags that are in either the term_id 224 or 229
I am trying to make it so that it only returns tags that are in BOTH 224 and 229
I've tried
t1.taxonomy = 'product_cat' AND p1.post_status = 'publish' AND terms1.term_id = '224' AND
t1.taxonomy = 'product_cat' AND p1.post_status = 'publish' AND terms1.term_id = '229' AND
but it just returns an empty result...
The only solution I have right now is to do two queries terms1.term_id = '224' for 224 and terms1.term_id = '229' for 229 and then use other logic to return the two matching.
Is there a way to combine this into one query?
Edit: See results of the IN query below (modified so that terms1.term_id is visible)
As you can see Gray is only in 229 (and thus not part of the result set I am trying to return back)
#BrianBruman I'm assuming that terms1.term_id is a comma separated list of values. If this is the case, you can use FIND_IN_SET() to make sure both 224 and 229 are in the comma delimited list.
Here is what your query needs to look like in order to get tags with both 224 and 229:
SELECT DISTINCT terms2.term_id as tag_id,
terms2.name as tag_name,
terms2.slug as tag_slug,
t2.description as tag_desc
FROM
wp_posts as p1
LEFT JOIN wp_term_relationships as r1 ON p1.ID = r1.object_ID
LEFT JOIN wp_term_taxonomy as t1 ON r1.term_taxonomy_id = t1.term_taxonomy_id
LEFT JOIN wp_terms as terms1 ON t1.term_id = terms1.term_id
JOIN wp_posts as p2 ON p2.ID = p1.ID
LEFT JOIN wp_term_relationships as r2 ON p2.ID = r2.object_ID
LEFT JOIN wp_term_taxonomy as t2 ON r2.term_taxonomy_id = t2.term_taxonomy_id
LEFT JOIN wp_terms as terms2 ON t2.term_id = terms2.term_id
WHERE
t1.taxonomy = 'product_cat'
AND p1.post_status = 'publish'
AND (
FIND_IN_SET('224', terms1.term_id) > 0
AND FIND_IN_SET('229', terms1.term_id) > 0
)
AND t2.taxonomy = 'product_tag'
AND p2.post_status = 'publish'
ORDER by tag_name
EDIT: according to your last comment, terms1.term_id isn't a comma delimited list. In that case, you would need to join in another instance of wp_terms. Here is what your query needs to look like:
SELECT DISTINCT terms2.term_id as tag_id,
terms2.name as tag_name,
terms2.slug as tag_slug,
t2.description as tag_desc
FROM
wp_posts as p1
LEFT JOIN wp_term_relationships as r1 ON p1.ID = r1.object_ID
LEFT JOIN wp_term_taxonomy as t1 ON r1.term_taxonomy_id = t1.term_taxonomy_id
LEFT JOIN wp_terms as terms1 ON t1.term_id = terms1.term_id
LEFT JOIN wp_terms as terms3 ON t1.term_id = terms3.term_id
JOIN wp_posts as p2 ON p2.ID = p1.ID
LEFT JOIN wp_term_relationships as r2 ON p2.ID = r2.object_ID
LEFT JOIN wp_term_taxonomy as t2 ON r2.term_taxonomy_id = t2.term_taxonomy_id
LEFT JOIN wp_terms as terms2 ON t2.term_id = terms2.term_id
WHERE
t1.taxonomy = 'product_cat'
AND p1.post_status = 'publish'
AND terms1.term_id = '224'
AND terms3.term_id = '229'
AND t2.taxonomy = 'product_tag'
AND p2.post_status = 'publish'
ORDER by tag_name
One last thing: if you only want results to show where wp_posts has a corresponding wp_terms record where the term_id is both 224 and 229 you shouldn't be using LEFT JOIN on wp_terms, you should use a regular old JOIN, which will improve the performance of your query a lot.
Hope this helps!

MySQL: Using JOIN statement in a subqery [duplicate]

This question already has answers here:
Referencing a query result alias in a subquery
(3 answers)
Closed 2 years ago.
Joins can be formulated in an explicit syntax ([INNER|LEFT|OUTER|..] JOIN... ON...) or specifying the conditions in the WHERE statement.
How can I refer to a table called in the external FROM statement and JOIN it in the subquery?
In this case I'm referring to the table with the alias p
SELECT
p.id,
p.post_title,
(SELECT
GROUP_CONCAT(DISTINCT wp_terms.name
SEPARATOR ',')
FROM
p
JOIN
wp_term_relationships ON (p.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_terms ON (wp_terms.term_id = wp_term_taxonomy.term_id)
AND wp_term_taxonomy.taxonomy IN ('post_tag' , 'category'))
FROM
`post_senza_revisioni` p
WHERE
p.post_type = 'post'
AND p.post_status = 'publish'
AND p.post_parent = 0
GROUP BY id , post_title
I'm pretty sure you want a correlated subquery:
SELECT p.id, p.post_title,
(SELECT GROUP_CONCAT(DISTINCT wp_terms.nameSEPARATOR ',')
FROM wp_term_relationships tr
wp_term_taxonomy tt
ON tr.term_taxonomy_id = tt.term_taxonomy_id LEFT JOIN
wp_terms t
ON t.term_id = tt.term_id AND
tt.taxonomy IN ('post_tag' , 'category'))
WHERE p.id = r.object_id
)
FROM `post_senza_revisioni` p
WHERE p.post_type = 'post' AND
p.post_status = 'publish' AND
p.post_parent = 0;
I doubt the GROUP BY is needed in the outer query, so I removed it. If you do have duplicate id values in the p table, then you can add it back in -- although that suggests that id is a really bad name for the column.
Without knowing the data you, I think you should be able to use the sub query in the FROM clause, join on the ID and then just select * or your desired columns from the sub query.
SELECT
p.id,
p.post_title,
sub.*
FROM
`post_senza_revisioni` p
JOIN (SELECT
GROUP_CONCAT(DISTINCT wp_terms.name
SEPARATOR ',')
FROM
p
JOIN
wp_term_relationships ON (p.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_terms ON (wp_terms.term_id = wp_term_taxonomy.term_id)
AND wp_term_taxonomy.taxonomy IN ('post_tag' , 'category')
) sub
ON p.id = sub.id
WHERE
p.post_type = 'post'
AND p.post_status = 'publish'
AND p.post_parent = 0
GROUP BY id , post_title

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?

Select Post with the term id

I am having a situation where i need to select a post but with where condition as shown :-
Post with both the selected terms at once.
I have tried :-
SELECT p.ID, p.post_title FROM wp_posts p
LEFT JOIN `wp_term_relationships` t
ON p.ID = t.object_id
LEFT JOIN `wp_term_taxonomy` tt
ON t.term_taxonomy_id = tt.term_taxonomy_id
WHERE tt.term_id =86
AND tt.term_id=39
GROUP BY t.object_id
HAVING COUNT( t.term_taxonomy_id ) =2
LIMIT 0,7
Here i want to select a post which is having the term id 86 & 39. These both ids are in same table.
What is the relationship between these tables?
This select works, but I think you can try another way, do you have the TAG instead of the code? Anyway, check this out.
SELECT
p.ID
FROM
wp_posts p
LEFT JOIN wp_term_relationships t ON (p.ID = t.object_id)
WHERE
exists (
SELECT tt.term_taxonomy_id FROM wp_term_taxonomy tt
WHERE tt.term_taxonomy_id = t.term_taxonomy_id
and tt.term_id in(86,39)
)
group by p.ID
having count(p.ID) = 2
Use the IN clause
SELECT p.ID
,p.post_title
FROM wp_posts p
LEFT JOIN 'wp_term_relationships' t ON p.ID = t.object_id
LEFT JOIN 'wp_term_taxonomy' tt ON t.term_taxonomy_id = tt.term_taxonomy_id
WHERE tt.term_id IN (86,39)
GROUP BY t.object_id
HAVING COUNT(t.term_taxonomy_id) = 2 LIMIT 0,7
You can write your where condition as follow:
WHERE tt.term_id in (86,39)

Wordpress custom query: cross-reference custom taxonomy from one post type with custom meta from another

I'm having trouble with a custom Wordpress MySQL query. My situation is this: I have the regular post type 'post' for posts and a custom post type, 'authors', for information about authors. The 'authors' post type holds all authors, though not all need be authors of posts.
Each post (of types 'post' and 'author') has a custom taxonomy with the author's exact name (eg. "John Smith"). The post type 'author' has additional custom meta values for the author's first name and last name (as they can become complex and for easier ordering by last name, first name).
Now I'm trying to select all authors who've published a post, count their number of posts and show the meta values, associated with their name. I'm uncertain as to how to cross-reference the taxonomy from one post type ('post') with the meta values form another ('author') in one row.
What I want:
John Smith John Smith 10
What I've got so far:
John Smith 10 John
John Smith 10 Smith
I'm stuck with the query below. Any help will be much appreciated!
SELECT
N, C, meta_value
FROM
(SELECT
t.name AS N, count(*) AS C
FROM
wp_2_posts p
INNER JOIN wp_2_term_relationships AS tr ON p.ID=tr.object_id
INNER JOIN wp_2_term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
INNER JOIN wp_2_terms AS t ON tt.term_id = t.term_id
WHERE 1=1
AND tt.taxonomy = 'myauthor'
AND p.post_type = 'post'
AND p.post_status = 'publish'
GROUP BY
N
ORDER BY
C DESC) AS x
INNER JOIN wp_2_posts p2
INNER JOIN wp_2_term_relationships AS tr2 ON p2.ID=tr2.object_id
INNER JOIN wp_2_term_taxonomy AS tt2 ON tr2.term_taxonomy_id = tt2.term_taxonomy_id
INNER JOIN wp_2_terms AS t2 ON tt2.term_id = t2.term_id
INNER JOIN wp_2_postmeta AS m2 ON m2.post_id = p2.ID
WHERE 1=1
AND post_type = 'author'
AND t2.name = x.N
AND (m2.meta_key = 'lastname' OR m2.meta_key = 'firstname')
Here's a rather ungly solution, any ideas on how to improve it?
SELECT
N, C, m2.meta_value AS L, m3.meta_value AS F
FROM
(SELECT
t.name AS N, count(*) AS C
FROM
wp_2_posts p
INNER JOIN wp_2_term_relationships AS tr ON p.ID=tr.object_id
INNER JOIN wp_2_term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
INNER JOIN wp_2_terms AS t ON tt.term_id = t.term_id
WHERE 1=1
AND tt.taxonomy = 'myauthor'
AND p.post_type = 'post'
AND p.post_status = 'publish'
GROUP BY
N) AS x
INNER JOIN wp_2_posts p2
INNER JOIN wp_2_term_relationships AS tr2 ON p2.ID=tr2.object_id
INNER JOIN wp_2_term_taxonomy AS tt2 ON tr2.term_taxonomy_id = tt2.term_taxonomy_id
INNER JOIN wp_2_terms AS t2 ON tt2.term_id = t2.term_id
INNER JOIN wp_2_postmeta AS m2 ON m2.post_id = p2.ID
INNER JOIN wp_2_posts p3
INNER JOIN wp_2_term_relationships AS tr3 ON p3.ID=tr3.object_id
INNER JOIN wp_2_term_taxonomy AS tt3 ON tr3.term_taxonomy_id = tt3.term_taxonomy_id
INNER JOIN wp_2_terms AS t3 ON tt3.term_id = t3.term_id
INNER JOIN wp_2_postmeta AS m3 ON m3.post_id = p3.ID
WHERE 1=1
AND p3.post_type = 'author'
AND t3.name = x.N
AND m3.meta_key = 'firstname'
AND p2.post_type = 'author'
AND t2.name = x.N
AND m2.meta_key = 'lastname'
ORDER BY
C DESC,
L