please help me with my MySQL Query.
Basically I want to retrieve some users for a user-ranking-system. My project is based on a wordpress installation. But since my query is very specific, all wordpress methods and functions are useless.
I try to describe my query in words:
"Give me all Users, who have a minimum of three posts which have a minumum rating of 4 for each post. Furthermore each of the users post needs to have a minimum of 5 ratings."
(The date-conditions below in the query are unimportant, work perfectly)
For storing data, posts are saved as usual in wp_posts (special post_type). The amount of ratings for each post is stored with a meta value (key=rating_count). Each rating is stored in the table "wp_rating" (id, post_id, rating, user_id).
My Query looks like this:
SELECT SUM(r.rating) AS rating,
p.post_author AS user_id,
COUNT(r.rating) AS rating_count
FROM wp_rating r
INNER JOIN wp_posts p ON p.ID = r.post_id
INNER JOIN wp_postmeta meta ON meta.post_id = p.ID
WHERE p.post_status = 'publish'
AND (meta.meta_key = 'expiration_date'
AND meta.meta_value < DATE_FORMAT(NOW(), 'Ymd'))
AND (meta.meta_key = 'rating_count' AND meta.meta_value > 4)
AND MONTH(p.post_date) = 03
AND r.rating > 4
GROUP BY p.post_author
HAVING COUNT(p.ID) > 3
ORDER BY SUM(r.rating) DESC
The problem is the following line:
AND (meta.meta_key = 'rating_count' AND meta.meta_value > 4)
I dont get any results after firing this query. (I checked all posts twice for enough ratings)
I hope my description is okay for finding a solution.
Kind Regards,
Robin :-)
PS: Please help, Im very confused :(
At the same time column can be equal to one value meta.meta_key = 'expiration_date' or meta.meta_key = 'rating_count' how it is possible that at same time column can have these two values, i guess you have one to many relation between post and their meta you need another join to your wp_postmeta table to fullfill your second condition,and make sure you are comparing date to date type here meta.meta_value it smells like you have text type of meta.meta_value column
SELECT SUM(r.rating) AS rating,
p.post_author AS user_id,
COUNT(r.rating) AS rating_count
FROM wp_rating r
INNER JOIN wp_posts p ON p.ID = r.post_id
INNER JOIN wp_postmeta meta ON meta.post_id = p.ID
INNER JOIN wp_postmeta meta1 ON meta1.post_id = p.ID
WHERE p.post_status = 'publish'
AND (meta.meta_key = 'expiration_date'
AND meta.meta_value < DATE_FORMAT(NOW(), 'Ymd'))
AND (meta1.meta_key = 'rating_count' AND meta1.meta_value > 4)
AND MONTH(p.post_date) = 03
AND r.rating > 4
GROUP BY p.post_author
HAVING COUNT(p.ID) > 3
ORDER BY SUM(r.rating) DESC
i guess 'rating_count' you mean it the aliace you created before.
try that:
AND (meta.meta_key = COUNT(r.rating) AND meta.meta_value > 4)
Related
I've spent about 4 hours trying to figure out why I cannot do a group by of the post_title and left join the date data from the wp_postmeta table
The code without the group by clause, results all my events but I only want to retrieve the first instance of the event, by grouping by the post_title, which should give me the only first date in the string of classes to sign up for.
What am I doing wrong here?
SELECT posts.post_title,meta.date
FROM wp_posts posts
LEFT JOIN (
SELECT meta_value AS date,post_id FROM wp_postmeta
WHERE wp_postmeta.meta_key ='_EventStartDate'
) meta
ON posts.ID = meta.post_ID
WHERE posts.post_type='Tribe_Events' AND posts.post_status = 'publish'
GROUP BY posts.post_title
Currently meta.date is not in a GROUP BY, no math is performed on this field, and it is not part of a DISTINCT list of fields
It sounds like you want the first instance of wp_postmeta.meta_value for each wp_posts.post_title
If so, try this:
Ditch the subquery and resolve your meta.meta_key filter using COALESCE
Use the MIN function on meta.meta_value
If needed, CAST meta.meta_value as a datetime
Something like this maybe...harder to know without seeing the table structure and sample data.
SELECT posts.post_title
,MIN(CAST(meta.meta_value as DATETIME)) event_start_date
FROM wp_posts posts
LEFT JOIN wp_postmeta meta
ON posts.ID = meta.post_ID
WHERE posts.post_type ='Tribe_Events'
AND posts.post_status = 'publish'
AND COALESCE(meta.meta_key, 'UNKNOWN') ='_EventStartDate'
GROUP BY posts.post_title;
This version can cope with multiple instances of '_EventStartDate' per post_title
Not sure if this is needed but it causes no harm. Just think of it as wearing a belt and suspenders.
I have the following query which runs on a social network. The query fetches posts (like Facebook posts) from a database.
SELECT P.*,
P.id_post id_p,
PM.meta_content video_title,
PM2.meta_content video_views,
PM3.meta_content racebooking_views,
Greatest(P.creation_date, Coalesce(Max(C.date), P.creation_date)) AS
last_activity,
P.creation_date creation_date,
(SELECT Count(*)
FROM likes
WHERE post_id = P.id_post
AND post_type = 'P')
likes_count,
(SELECT Count(*)
FROM likes L
WHERE post_id = P.id_post
AND post_type = 'P'
AND L.id_profile = 2796)
do_i_like
FROM posts P
LEFT JOIN comments C
ON P.id_post = C.post_id
AND C.post_type = 'P'
AND C.id_profile != P.id_profile
LEFT JOIN post_meta PM
ON PM.id_post = P.id_post
AND PM.meta_type = 'T'
LEFT JOIN post_meta PM2
ON PM2.id_post = P.id_post
AND PM2.meta_type = 'V'
LEFT JOIN post_meta PM3
ON PM3.id_post = P.id_post
AND PM3.meta_type = 'W'
GROUP BY P.id_post
ORDER BY last_activity DESC
LIMIT 41, 10
Each post may have or may not have comments.
I want the query to fetch the post with the most recent activity first.
So, if the post has a comment, i take the date of the latest comment. If the post does not have a comment, i take the creation date of the post.
The job is done by Greatest(P.creation_date, Coalesce(Max(C.date), P.creation_date)) which picks up the greates value between the comments dates (if comments exist) and the post creation date.
Then, the ORDER BY last_activity DESC does the sorting job.
PROBLEM
The query is really slow. It takes 8 seconds to run. The posts table has 8K rows and the comments table has 8K rows.
What i don't understand is that if I replace the ORDER BY clause with this ORDER BY P.id_post it takes 0.5 seconds to run. But if I replace the ORDER BY clause with ORDER BY P.creation_date again it takes 8 seconds. It seems that it doesn't like dates...
Additional infos
posts table has an index on creation_date.
comments table has an index on date
server runs LAMP on CentOS Linux 6.6
I tried other solutions on SO like this one but they didn't work
How can i fix this query to run faster?
The correlated subqueries in the select clause are probably killing you. Instead, join to a subquery which computes likes statistics:
SELECT P.*,
P.id_post id_p,
PM.meta_content video_title,
PM2.meta_content video_views,
PM3.meta_content racebooking_views,
GREATEST(P.creation_date, COALESCE(MAX(C.date), P.creation_date)) AS last_activity,
P.creation_date creation_date,
t.likes_count,
t.do_i_like
FROM posts P
LEFT JOIN
(
SELECT
post_id,
SUM(CASE WHEN post_type = 'P' THEN 1 ELSE 0 END) AS likes_count,
SUM(CASE WHEN post_type = 'P' AND L.id_profile = 2796
THEN 1 ELSE 0 END) AS do_i_like
FROM likes
GROUP BY post_id
) t
ON t.post_id = P.id_post
LEFT JOIN comments C
ON P.id_post = C.post_id AND
C.post_type = 'P' AND
C.id_profile != P.id_profile
LEFT JOIN post_meta PM
ON PM.id_post = P.id_post AND
PM.meta_type = 'T'
LEFT JOIN post_meta PM2
ON PM2.id_post = P.id_post AND
PM2.meta_type = 'V'
LEFT JOIN post_meta PM3
ON PM3.id_post = P.id_post AND
PM3.meta_type = 'W'
ORDER BY
last_activity DESC
LIMIT 41, 10
Also after editing your query I do not see a reason to be using GROUP BY in the outer query, so I removed it. And you should be using indices where appropriate, though my hunch is that my suggestion alone should give a noticeable performance boost.
There is a MAX(C.Date) that would require a group by clause, however it too could be substituted for a subquery I believe:
SELECT P.*,
P.id_post id_p,
PM.meta_content video_title,
PM2.meta_content video_views,
PM3.meta_content racebooking_views,
GREATEST(P.creation_date, COALESCE(max_c_date, P.creation_date)) AS last_activity,
P.creation_date creation_date,
t.likes_count,
t.do_i_like
FROM posts P
LEFT JOIN
(
SELECT
post_id,
SUM(CASE WHEN post_type = 'P' THEN 1 ELSE 0 END) AS likes_count,
SUM(CASE WHEN post_type = 'P' AND L.id_profile = 2796
THEN 1 ELSE 0 END) AS do_i_like
FROM likes
GROUP BY post_id
) t
ON t.post_id = P.id_post
LEFT JOIN (
SELECT
comments.post_id,
MAX(comments.date) max_c_date
FROM comments
inner join posts ON comments.post_id = posts.id_post
where comments.post_type = 'P' AND
comments.id_profile != posts.id_profile
GROUP BY comments.post_id
) C
ON P.id_post = C.post_id AND
LEFT JOIN post_meta PM
ON PM.id_post = P.id_post AND
PM.meta_type = 'T'
LEFT JOIN post_meta PM2
ON PM2.id_post = P.id_post AND
PM2.meta_type = 'V'
LEFT JOIN post_meta PM3
ON PM3.id_post = P.id_post AND
PM3.meta_type = 'W'
ORDER BY
last_activity DESC
LIMIT 41, 10
I have a posts table with id column and more, and a votes table with post_id, value columns and more. Each post_id can be repeated in the votes table.
Now I want to select the most voted posts (and the number of votes) from the database, and I've tried the next:
$query = "SELECT p, SUM(v.value) FROM {$wpdb->posts} p, wp_wti_like_post v JOIN p.id v.post_id WHERE 1=1";
$myrows = $wpdb->get_results( $query );
var_dump($myrows);
but it retrieves an empty array.
Note: {$wpdb->posts} is the correct table for the posts
You need to group by post.id to get a proper sum per post. Then you can sort descending by that sum and get the first row (= highest value).
SELECT
p.*, /* Not sure if this will work. Maybe you have to
specify exact fields you need, although MySQL
is pretty forgiving. */
SUM(v.value) AS number_of_votes
FROM
{$wpdb->posts} p
INNER JOIN wp_wti_like_post v ON v.post_id = p.id
GROUP BY
p.id
ORDER BY
SUM(v.value) DESC
LIMIT 1";
I haven't tested that, but it should work for you.
SELECT {$wpdb->posts}.*,wp_wti_like_post.*, SUM(wp_wti_like_post.value) as Svalue
FROM {$wpdb->posts}
JOIN wp_wti_like_post ON wp_wti_like_post.post_id = {$wpdb->posts}.id
I have a query that creates a table view and then another that queries the view. The results are extremely slow.
Here is the code:
create or replace view $view_table_name as select * from wp_2_postmeta where post_id IN (
select ID FROM wp_2_posts wposts
LEFT JOIN wp_2_term_relationships ON (wposts.ID = wp_2_term_relationships.object_id)
LEFT JOIN wp_2_term_taxonomy ON (wp_2_term_relationships.term_taxonomy_id = wp_2_term_taxonomy.term_taxonomy_id)
WHERE wp_2_term_taxonomy.taxonomy = 'category'
AND wp_2_term_taxonomy.parent = $cat || wp_2_term_taxonomy.term_id = $cat
AND wposts.post_status = 'publish'
AND wposts.post_type = 'post')
The $values have been put it in for this example that queries the view table for the results.
select distinct(ID)
FROM $view_table_name wposts
LEFT JOIN wp_2_postmeta wpostmeta
ON wposts.ID = wpostmeta.post_id
WHERE post_status = 'publish'
AND ID NOT IN (SELECT post_id
FROM wp_2_postmeta
WHERE meta_key = '$var' && meta_value = '$value1')
AND ID NOT IN (SELECT post_id
FROM wp_2_postmeta
WHERE meta_key = '$var' && meta_value = '$value2')
AND ID NOT IN (SELECT post_id
FROM wp_2_postmeta
WHERE meta_key = '$var' && meta_value = '$value3')
AND postmeta.meta_key = 'pd_form'
ORDER BY CASE wpostmeta.meta_value
WHEN '$value5' THEN 1
WHEN '$value6' THEN 2
WHEN '$value7' THEN 3
WHEN '$value8' THEN 4
WHEN '$value9' THEN 5
WHEN '$value10' THEN 6
WHEN '$value11' THEN 7
WHEN '$value11' THEN 8
END;
The main problem here is a subquery in IN condition. Instead executing the subquery and then checking in the outer table for correspondences, MySQL is known to transform the query into a correlated subquery which is executed for each row in the outer table.
The usual solution is to get rid of the subquery in the IN in favour of a JOIN.
Another problem is that you use OUTER JOIN instead of inner JOIN though you do not actually need it (MySQL is usually smart enough to optimize it when it is trivial, but anyway you should express your intention more clearly).
And one more thing. Both queries seem to be dynamically generated. Beside optimizing the query itself, one should think how not to break the calling code. That may be tricky though.
Optimizing wordpress is always an interesting challenge.
On the home page of my website I want to display the latest posts to the forum however I don't want to show the same topic twice. How can I modify the code below to do this?
http://punbb.informer.com/wiki/punbb13/integration#recent_10_posts
Basically show the latest posts, but only once for each forum topic/thread.
Add a condition to keep only records where the post is the last post in the topic:
WHERE p.id = (
SELECT pp.id
FROM posts AS pp
WHERE pp.topic_id = t.id ORDER BY pp.posted DESC LIMIT 1
)
If you want only one value per topic, you could group by topic, and from each topic select the most recent post. Then, you could choose the top 10 topics.
I'll write it in SQL, and you can translate that to PHP:
SELECT p.id, p.message, o.subject
FROM
((SELECT t.id
FROM posts AS p LEFT JOIN topics AS t ON p.topic_id = t.id
GROUP BY t.id
HAVING p.posted = MAX(p.posted) ) ids LEFT JOIN topics AS t ON ids.id = t.id) o
LEFT JOIN posts AS p ON o.id = posts.topic_id
ORDER BY p.posted DESC
LIMIT '0,10'
change this line
'SELECT' => 'p.id, p.message, t.subject',
to
'SELECT DISTINCT' => 'p.id, p.message, t.subject',