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
Related
Hi there so first of all , i'm not that expert in mysql queries. i have tow tables wp_posts and wp_wti_like_post and i'm doing LEFT JOIN on wp_wti_like_post ON wp_posts.ID = wp_wti_like_post.post_id and SUM(wp_wti_like_post.value) < 2 BUT if there is no rowin wp_wti_like_post with id of post from wp_posts then it doesn't show even the row from wp_posts and just ignore it , please help in this really need it.
query:
SELECT *
FROM wp_posts
LEFT JOIN wp_wti_like_post ON wp_posts.ID = wp_wti_like_post.post_id
WHERE wp_posts.post_status = 'publish'
GROUP BY wp_wti_like_post.post_id
HAVING SUM( wp_wti_like_post.value ) <2
OR SUM( wp_wti_like_post.value ) = NULL
LIMIT 0 , 200
table wp_wti_like_post
http://prntscr.com/6xixrd
table wp_posts
http://prntscr.com/6xixzp
You might need to restructure your query to as follows:
SELECT * FROM
(
SELECT WP.*, SUM(LP.value) AS `value`
FROM wp_posts WP
LEFT JOIN
(SELECT post_id FROM wp_wti_like_post WHERE post_status = 'publish') LP
ON WP.ID = LP.post_id
GROUP BY WP.ID
) T1
WHERE T1.value IS NULL
OR T1.value < 2;
The Inner-most query first fetches only wti_like_posts that have been published.
The left-join is then performed, which will give you the expected result of fetching all rows from wp_posts, even if they do not join with a record from the sub-query.
After that is performed, the GROUP statement performs the computation of SUMming the values.
The outer-most query enforces the requirement that the sum must either be less than 2 or be null.
So i sorted this out by my self and go it working in this way
at the place of
SUM( wp_wti_like_post.value ) = NULL
it should need to be
SUM( wp_wti_like_post.value ) IS NULL
WordPress's wp_postmeta table has all the additional fields for a post but they are in rows so it's easy to add more.
However, now I want to query for all the fields of all the posts lets say, I obviously want those fields in a column and not a row.
This is my query that I am running
SELECT p.post_title,
m.meta_value,
m.meta_key
FROM wp_posts p
JOIN wp_postmeta m
ON p.id = m.post_id
WHERE p.id = 72697;
This will give me all the meta_values and their respective meta keys as columns. But I need the meta keys values as columns and meta values as rows
For example a meta_key could be additional_description and it's value could be What's up
So I need something like this
SELECT p.post_title, additional_description
FROM wp_posts p
JOIN wp_postmeta m
ON p.id = m.post_id
WHERE p.id = 72697;
I need it as a column. I also need all of the posts and not a specific one, but whenever I remove the where it just doesn't query (I have lots of posts, that could be an issue).
Here is some sample data and how I want the results to show up
wp_postmeta table
meta_key post_id meta_key meta_value
1 5 total_related 5
2 5 updated 0
3 5 cricket 1
4 8 total_related 8
5 8 updated 1
6 8 cricket 0
wp_post table
id post_title other things I dont care about
5 This is awesome
8 This is more awesome
wp_post id is related to post_id on wp_postmeta table
Result wanted
post_title total_related updated cricket
This is awesome 5 0 1
This is more awesome 8 1 0
What about something like this?
SELECT p.post_title, m1.meta_value as 'total_related', m2.meta_value as 'updated', m3.meta_value as 'cricket'
FROM wp_posts p
LEFT JOIN wp_postmeta m1
ON p.id = m1.post_id AND m1.meta_key = 'total_related'
LEFT JOIN wp_postmeta m2
ON p.id = m2.post_id AND m2.meta_key = 'updated'
LEFT JOIN wp_postmeta m3
ON p.id = m3.post_id AND m3.meta_key = 'cricket'
And since you aren't looking for a specific post you should be able to do this.
If you want to query specific post_types you can try something like this
SELECT p.post_title, m1.meta_value as 'total_related', m2.meta_value as 'updated', m3.meta_value as 'cricket'
FROM wp_posts p
LEFT JOIN wp_postmeta m1
ON p.id = m1.post_id AND m1.meta_key = 'total_related'
LEFT JOIN wp_postmeta m2
ON p.id = m2.post_id AND m2.meta_key = 'updated'
LEFT JOIN wp_postmeta m3
ON p.id = m3.post_id AND m3.meta_key = 'cricket'
WHERE p.post_type = 'my_custom_post_type';
Try that:
select post_title ,
MAX(CASE WHEN `meta_key`='total_related' THEN meta_value END)as 'total_related',
MAX(CASE WHEN `meta_key` = 'updated' THEN meta_value END) as 'updated' ,
MAX(CASE WHEN `meta_key` = 'cricket' THEN meta_value END) as 'cricket'
FROM wp_posts p
JOIN wp_postmeta m ON p.id = m.post_id
GROUP BY p.id
There are several approaches.
Here's an example of one way to get the specified result, using correlated subqueries in the SELECT list:
SELECT p.post_title
, ( SELECT m1.meta_value
FROM wp_post_metadata m1
WHERE m1.meta_key = 'total_related'
AND m1.post_id = p.id
ORDER BY m1.meta_key LIMIT 1
) AS `total_related`
, ( SELECT m2.meta_value
FROM wp_post_metadata m2
WHERE m2.meta_key = 'updated'
AND m2.post_id = p.id
ORDER BY m2.meta_key LIMIT 1
) AS `updated`
, ( SELECT m3.meta_value
FROM wp_post_metadata m3
WHERE m3.meta_key = 'cricket'
AND m3.post_id = p.id
ORDER BY m3.meta_key LIMIT 1
) AS `cricket`
FROM wp_posts p
WHERE p.id IN (5,8)
There are several other approaches, each with its own advantages and drawbacks.
There's a somewhat related question I referenced in a comment on the question. That question illustrates several approaches, but omits a correlated subquery approach.)
Here's how I did this dynamically - this procedure builds a SQL statement for every postmeta key for a given post type and then runs the "pivot" query for you:
This isn't the fastest query, and we use it only for migration and deep dives into data, but it does the job.
Note that this temporarily resets the max length of the concat function so you can build a large SQL statement:
CREATE PROCEDURE `wp_posts_pivot`(IN post_type_filter varchar(50))
BEGIN
/* allow longer concat */
declare max_len_original INT default 0;
set max_len_original = ##group_concat_max_len;
set ##group_concat_max_len=100000;
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT CONCAT('MAX(IF(pm.meta_key = ''',
meta_key,
''', pm.meta_value, NULL)) AS `',
meta_key,
'`'))
INTO #sql FROM
wp_posts p
INNER JOIN
wp_postmeta AS pm ON p.id = pm.post_id
WHERE
p.post_type = post_type_filter;
SET #sql = CONCAT('SELECT p.id
, p.post_title
, ', #sql, '
FROM wp_posts p
LEFT JOIN wp_postmeta AS pm
ON p.id = pm.post_id
where p.post_type=\'',post_type_filter,'\'
GROUP BY p.id, p.post_title');
/* reset the default concat */
set ##group_concat_max_len= max_len_original;
/*
select #sql;
*/
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END
You can then call this with a simple call such as this one, which will select a single row for each 'page' post type along with all meta values:
call wp_posts_pivot('page');
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)
I'm asking for query statement I have two tables
first table 'posts' :
post_id title Userid
1 test 1
The other table is 'likes'
userid post_id
1 1
I need single query to check if user like this photo or not in bulk select my solution is very poor :
SELECT * FROM `table` WHERE x = x LIMIT 100;
and give it foreach and make query for every row :
foreach($results as $v)
{
$data[1] = $v;
$data[1]['is_like'] = SELECT COUNT(*) FROM likes WHERE userid = 1;
}
1 is the already login user id
I need to single query to return post_id,.. etc and filed like is_like
Assuming likes can have only 1 row with the same (user_id,post_id) :
SELECT p.* ,
CASE WHEN
l.id IS NULL THEN 0
ELSE 1
END as is_liked
FROM posts p
LEFT JOIN likes l ON l.user_id = p.user_id and l.post_id =p.post_id
If not (multiple rows in likes for a given (user_id,post_id) ) :
SELECT p.* ,
CASE WHEN
l.user_id IS NULL THEN 0
ELSE 1
END as is_liked
FROM posts p
LEFT JOIN
(
SELECT DISTINCT user_id,post_id FROM likes
) l ON l.user_id = p.user_id and l.post_id =p.post_id;
Or
SELECT p.* ,
CASE
WHEN EXISTS (SELECT NULL FROM likes l
WHERE l.user_id = p.user_id and l.post_id =p.post_id) THEN 1
ELSE 0
END as is_liked
FROM posts p
Update
I hope I got a better understanding of the question now. My assumption : posts.user_id is id of user who created post; like table stores information about who likes the post. Thus, to check all posts and whether a particular like them you need ($login_user_id should be escaped properly)
SELECT p.* ,
CASE
WHEN EXISTS (SELECT NULL FROM likes l
WHERE l.user_id = $login_user_id and l.post_id =p.post_id) THEN 1
ELSE 0
END as is_liked
FROM posts p
SELECT
p.post_id,
p.title,
IF(l.post_id IS NOT NULL,1,0) as like
FROM posts as p
LEFT JOIN likes as l ON l.post_id = p.post_id AND l.userid = p.userid
WHERE p.Userid = 1
If post_id in likes table is available it will return 1 else 0.
Something like this should work as a single query (untested, of course - you didn't provide much data to use for testing):
select
p.title, count(l.post_id)
from
`posts` p
inner join
`likes` l
on
l.userid = p.userid and l.post_id = p.post_id
where
p.userid = loggedinuserID
group by
p.userid
This will give you a count of the total posts the specified user has liked.
In my home page I show different posts from different categories.
My current implementation is to call query_posts many times (once per category)
How can I use one query to pull out the data?
My tries
1 - this method works(ignore the ugly very long sql,I have many categories...)
Thanks the post: http://www.xaprb.com/blog/2006/12/07/how-to-select-the-firstleastmax-row-per-group-in-sql/
( SELECT *
FROM `wp_posts` p,`wp_term_relationships` rel
WHERE rel.object_id = p.ID
AND rel.term_taxonomy_id = '3'
ORDER BY p.post_date DESC
LIMIT 2)
UNION ALL
( SELECT *
FROM `wp_posts` p,`wp_term_relationships` rel
WHERE rel.object_id = p.ID
AND rel.term_taxonomy_id = '4'
ORDER BY p.post_date DESC
LIMIT 2)
SELECT i.*
FROM wp_term_relationships rel
INNER JOIN (
SELECT p.*, rel2.*
FROM wp_posts p
INNER JOIN wp_term_relationships rel2
ON (rel2.object_id = p.ID)
WHERE rel2.term_taxonomy_id = rel.term_taxonomy_id
ORDER BY p.post_date DESC
LIMIT 2 OFFSET 0 ) i ON (i.object_id = rel.object_id)
WHERE rel.term_taxonomy_id IN ('3','4')
ORDER BY i.term_taxonomy_id ASC, i.post_date DESC