Basically I want to select all the posts that have at least one comment and get the comment count for each. What I cam up with so far is only giving me one result, which is has an accurate count but there are more than one posts that have comments. Can any recommend changes to make this work
SELECT
posts.id,
COUNT(DISTINCT comments.post_id) AS count
FROM
posts
LEFT JOIN comments ON posts.id = comments.post_id
Using aggregate functions you must group them see here GROUP BY (Aggregate) Functions for the posts must contain atleast one comment you can use HAVING count >= 1
SELECT
posts.id,
COUNT(DISTINCT comments.post_id) AS `count`
FROM
posts
LEFT JOIN comments ON posts.id = comments.post_id
GROUP BY posts.id
HAVING `count` >= 1
You need a group by statement. And you can change the join to an inner join because you want only posts with comments:
SELECT p.id, COUNT(*) AS count
FROM posts p INNER JOIN
comments c
ON p.id = c.post_id
GROUP BY p.id;
The expression count(distinct c.post_id) will return 1 for each row, because there is only one distinct post id for each row. COUNT(*) will get the number of comments.
Related
I'm currently creating a small application where users can post a text which can be commented and the post can also be voted (+1 or -1).
This is my database:
Now I want to select all information of all posts with status = 1 plus two extra columns: One column containing the count of comments and one column containing the sum (I call it score) of all votes.
I currently use the following query, which correctly adds the count of the comments:
SELECT *, COUNT(comments.fk_commented_post) as comments
FROM posts
LEFT JOIN comments
ON posts.id_post = comments.fk_commented_post
AND comments.status = 1
WHERE posts.status = 1
GROUP BY posts.id_post
Then I tried to additionally add the sum of the votes, using the following query:
SELECT *, COUNT(comments.fk_commented_post) as comments, SUM(votes_posts.type) as score
FROM posts
LEFT JOIN comments
ON posts.id_post = comments.fk_commented_post
AND comments.status = 1
LEFT JOIN votes_posts
ON posts.id_post = votes_posts.fk_voted_post
WHERE posts.status = 1
GROUP BY posts.id_post
The result is no longer correct for either the votes or the comments. Somehow some of the values seem to be getting multiplied...
This is probably simpler using correlated subqueries:
select p.*,
(select count(*)
from comments c
where c.fk_commented_post = p.id_post and c.status = 1
) as num_comments,
(select sum(vp.type)
from votes_posts vp
where c.fk_voted_post = p.id_post
) as num_score
from posts p
where p.status = 1;
The problem with join is that the counts get messed up because the two other tables are not related to each tother -- so you get a Cartesian product.
You want to join comments counts and votes counts to the posts. So, aggregate to get the counts, then join.
select
p.*,
coalesce(c.cnt, 0) as comments,
coalesce(v.cnt, 0) as votes
from posts p
left join
(
select fk_commented_post as id_post, count(*) as cnt
from comments
where status = 1
group by fk_commented_post
) c on c.id_post = p.id_post
left join
(
select fk_voted_post as id_post, count(*) as cnt
from votes_posts
group by fk_voted_post
) v on v.id_post = p.id_post
where p.status = 1
order by p.id_post;
I have 2 tables, posts and comments
Posts
Post_Id
Content
Comments
Comment_id
Content
Post_id
I want to fetch the posts but I want the posts with highest number of comments to appear on top .
Please how do I do this
You can put a correlated subquery in the order by clause:
select p.*
from posts p
order by (select count(*) from comments c where c.post_id = p.post_id) desc
Another option is to pre-aggregate in a subquery. That's also handy if you want to dislay the count of comments:
select p.*, coalesce(c.cnt_comments, 0) cnt_comments
from posts p
left join (select post_id, count(*) cnt_comments from comments group by post_id) c
on c.post_id = p.post_id
order by coalesce(c.cnt_comments, 0) desc
I am making a forum page using MySQL as database, but I'm coming from MongoDB and am a bit confused. When I fetch all posts for a specific category it looks something like this
SELECT p.id, p.posted_at, p.title, p.content, c.name AS category_name, u.name
AS author_name
FROM posts AS p
INNER JOIN users AS u ON p.author = u.id
INNER JOIN categories AS c ON p.category = c.id
WHERE p.category = 3 <-- Category ID
People can follow posts so I have a table called user_post_relations which contains two columns; user_id and post_id.
My goal is to add a follower count per post to the query that's getting all the posts per category. How can this be achieved with only one query?
Add the following JOIN with sub-query to your query
JOIN (SELECT post_id, COUNT(*) follower_count
FROM user_post_relations
GROUP BY post_id) AS upr ON upr.post_id = p.id
And then add upr.follower_count to your SELECT list
I'm a little bit confused about a stupid query:
I get rows from the table posts joined with the table authors and the table comments, in a way like this:
SELECT posts.*, authors.name, COUNT(comments.id_post) AS num_comments
FROM posts JOIN authors ON posts.id_author = authors.id_author
LEFT JOIN comments ON posts.id_post = comments.id_post
WHERE posts.active = 1
AND comments.active = 1
this doesn't work, of course.
What I try to do is to retrieve:
1) all my active post (those that were not marked as deleted);
2) the names of their authors;
3) the number of active comments (those that were not marked as deleted) for each post (if there is at least one);
What's the way? I know it's a trivial one, but by now my brain is in offside…
Thanks!
Presumably, id_post uniquely identifies each row in posts. Try this:
SELECT p.*, a.name, COUNT(c.id_post) AS num_comments
FROM posts p JOIN
authors a
ON p.id_author = a.id_author LEFT JOIN
comments c
ON p.id_post = c.id_post
WHERE p.active = 1 AND c.active = 1
GROUP BY p.id_post;
Note that this uses a MySQL extension. In most other databases, you would need to list all the columns in posts plus a.name in the group by clause.
EDIT:
The above is based on your query. If you want all active posts with a count of active comments, just do:
SELECT p.*, a.name, SUM(c.active = 1) AS num_comments
FROM posts p LEFT JOIN
authors a
ON p.id_author = a.id_author LEFT JOIN
comments c
ON p.id_post = c.id_post
WHERE p.active = 1
GROUP BY p.id_post;
Since you are doing a count, you need to have a group by. So you will need to add
Group By posts.*, authors.name
You should you GROUP BY clause together with aggregate functions. Try something similar to:
SELECT posts.*, authors.name, COUNT(comments.id_post) AS num_comments
FROM posts JOIN authors ON posts.id_author = authors.id_author
LEFT JOIN comments ON posts.id_post = comments.id_post
-- group by
GROUP BY posts.*, authors.name
--
WHERE posts.active = 1
AND comments.active = 1
I found the correct solution:
SELECT posts.id_post, authors.name, COUNT(comments.id_post) AS num_comments
FROM posts JOIN authors
ON posts.id_author = authors.id_author
LEFT OUTER JOIN comments
ON (posts.id_post = comments.id_post AND comments.active = 1)
WHERE posts.active = 1
GROUP BY posts.id_post;
Thanks everyone for the help!
The following query selects all posts and each post's owner, all of the comments that belong to each post, and the owner of each comment.
I need to only retrieve 5 comments per post. I rewrote the query, but I get an error of "each derived table must have it's own alias".
SELECT posts.id AS postId, posts.body, users.id AS userId, users.displayname, comments.id AS commentId, comments.text, commenters.id, commenters.displayname
FROM posts
JOIN users ON posts.owneruserid = users.id
LEFT JOIN comments ON posts.id = comments.postid
JOIN users AS commenters ON comments.userId = commenters.id
ORDER BY posts.createdAt
New Query:
SELECT posts.id AS postId, posts.body, users.id AS userId, users.displayname
FROM posts
JOIN users ON posts.owneruserid = users.id
LEFT JOIN (
SELECT comments.id AS commentId, comments.text AS commentText, commenters.id AS commenterId, commenters.displayname AS commenterDisplayName
FROM comments
JOIN users AS commenters ON comments.userid = commenters.id
LIMIT 0,5
) AS comments ON comments.postid = posts.id
ORDER BY posts.createdAt
UPDATE The query now works, but it does not produce the desired output. I want to output 10 posts, with 5 comments for each post. This limit clause will only apply for the comments of the first post encountered.
From the edits and comment feedback, here's the query I think you are looking for... The inner most will prequery gets the posts and who initiated the post, comments and who posted the comments. This inner query is also pre-sorted with the MOST RECENT COMMENTS to the top per postID. Using the result of that, I'm joining to the sql variables (#variables) to get the #varRow increased every time a new comment and reset back to 1 each time a post ID changes (hence the inner PreQuery orders by post ID FIRST). Finally, using the HAVING clause to have the comment's #varRow count < 6 will get at MOST 5 of each post.
If you want to limit what posts you are trying to retrieve, I would apply a WHERE clause (such as date/time if available) at the INNER most that generates the "PreQuery".
select straight_join
PreQuery.*,
#varRow := if( #LastPost = PreQuery.PostID, #varRow +1, 1 ) CommentRow,
#LastPost := PreQuery.PostID PostID2
from
( select
posts.id PostID,
posts.body,
posts.CreatedAt,
u1.id UserID,
u1.DisplayName NameOfPoster,
c.id,
c.userid CommentUserID,
c.text CommentText,
u2.DisplayName CommentUserName
from
posts
join users u1
on posts.ownerUserID = u1.id
LEFT JOIN comments c
on posts.id = c.PostID
join users u2
on c.userid = u2.id
where
posts.id = TheOneParentIDYouWant
OR posts.parentid = TheOneParentIDYouWant
order by
posts.ID,
c.id desc ) PreQuery,
(select #varRow := 0, #LastPost = 0 ) SQLVars
having
CommentRow < 6
order by
PreQuery.postid,
CommentRow
--- EDIT --- per comment
I THINK what you mean by which "Parent Post" the comments are associated with is because they have the post ID directly. Since the inner-most query does a join of all elements / tables across the board, all are coming along for the ride...
Post -> User (to get posting user name )
Post -> Comment (on Common Post ID -- left joined)
Comment -> User ( to get commenting user name)
Once THAT is all done and sorted by common Post ID and most recent comment sorted to the top, I then apply the #vars against ALL returned rows. The HAVING clause will strip out any comment where it's sequence is BEYOND the 5 you were looking for.
You need to give your derived table an alias:
SELECT posts.id AS postId, posts.body, users.id AS userId, users.displayname
FROM posts
JOIN users ON posts.owneruserid = users.id
LEFT JOIN (
SELECT comments.id AS commentId, comments.text AS commentText, commenters.id AS commenterId, commenters.displayname AS commenterDisplayName
FROM comments
JOIN users AS commenters ON comments.userid = commenters.id
LIMIT 0,5
) AS derived_table_alias
ORDER BY posts.createdAt
Since you're using a subquery (which is what it means by "derived table"), it must indeed have an alias. Thus, all you need to do is:
SELECT posts.id AS postId, posts.body, users.id AS userId, users.displayname
FROM posts
JOIN users ON posts.owneruserid = users.id
LEFT JOIN (
SELECT comments.id AS commentId, comments.text AS commentText, commenters.id AS commenterId, commenters.displayname AS commenterDisplayName
FROM comments
JOIN users AS commenters ON comments.userid = commenters.id
LIMIT 0,5
) as some_alias --This is what's triggering the error
ORDER BY posts.createdAt
Even if you're not selecting from the subquery, and just using it as a filter, you have to alias it.
By Error
Add an alias following your subquery.
Example: SELECT * FROM foo JOIN (select * from bar) AS <alias_here>
Make sure you have a field in the posts table and that it is called createdAt. I'm not sure MySQL is case-sensitive, but the error you posted says createdat (with the 'A' lowercased)
You have two LEFT JOINs, but only one ON statement. A join isn't anything without the hook to join it on. Example:
SELECT *
FROM foo JOIN bar ON (foo.id=bar.id)
LEFT JOIN (select * from foobar) AS baz **ON foo.id=baz.id**
In order to join on a field, the field needs to be present in the table involved in the join. So in the above example, if you match foo.id with baz.id, id needs to be returned in the subquery (baz).