How can I group mysql results based on a calculated colum? - mysql

I'm trying to show the most commented posts of my web but is being impossible .-. I just got this mysql error all the time (Can't group on 'comments')
the relation between tables is:
table: post / colums: id_post, title, id_comment
table : comment / colums: id_comment, text, id_post
and this is the query I'm trying to use
SELECT p.title AS title, COUNT(c.id_comment) AS comments
FROM post p
INNER JOIN comment c ON p.id_post=c.id_post
GROUP BY comments DESC
Please any alternative or solution for this?

Why would you want to group by on the number of comments? You need order by clause to get the most commented to the top and group by posts:
SELECT p.title AS title, COUNT(c.id_comment) AS comments
FROM post p
INNER JOIN comment c ON p.id_post=c.id_post
GROUP BY p.id_post, p.title
ORDER BY comments DESC
You may want to have a limit clause as well to get the top N commented posts only.

Related

I want to get all posts which has more than 250 likes(likes are stored in different tabe)

I have two mysql database table's posts and post_likes...I want to get all posts which has more than 250 likes
right now i am using this query :-
SELECT posts.*, #total_likes := COUNT(nft_likes.id) as total_likes FROM posts inner join nft_likes on nft_likes.nft_id=posts.auction_id where #total_likes>1 group by posts.id
This is the first time i have asked a question.so pls forgive for bad way of telling
post_likes table schema
post table schema
In the WHERE clause you can only refer to a row's data. The result of a COUNT, however, refers to the aggregation of several rows. Use the HAVING clause for limitations on these results.
SELECT
p.*,
COUNT(l.id) AS total_likes
FROM posts p
INNER JOIN nft_likes l ON l.nft_id = p.auction_id
GROUP BY p.id
HAVING COUNT(l.id) > 1
ORDER BY p.id;

Avoiding the “n+1 selects” problem when I want a subset of related resources

Imagine I'm designing a multi-user blog and I have user, post, and comment tables with the obvious meanings. On the main page, I want to show the ten most recent posts along with all their related comments.
The naive approach would be to SELECT the ten most recent posts, probably JOINed with the users that wrote them. And then I can loop through them to SELECT the comments, again, probably JOINed with the users that wrote them. This would require 11 selects: 1 for the posts and 10 for their comments, hence the name of the famous anti-pattern: n+1 selects.
The usual advice for avoiding this anti-pattern is to use the IDs from the first query to fetch all related comments in a second query which may look something like this:
SELECT
*
FROM
comments
WHERE
post_id IN (/* A comma separated list of post IDs returned from the first query */)
As long as that comma separated list is in reasonably short we managed to fetch all the data we need by issuing only two SELECT queries instead of eleven. Great.
But what if I only want the top three comments for each post? I didn't try but I can probably come up with some LEFT JOIN trickery to fetch the most recent posts along with their top three comments in a single query but I'm not sure it would be scalable. What if I want the top hundred comments which would exceed the join limit of 61 tables of a typical MySQL installation for instance?
What is the usual solution for this other than reverting to n+1 selects anti-pattern? What is the most efficient way to fetch items with a subset of items related to each one in this fairly typical scenario?
It is usually a better option to run as few queries as possible, and then implement some application logic on top of it if needed. In your use case, I would build a query that returns both the most recent posts and the most recent associated comments, with proper ordering to make the application processing easier. Then your application can take care of displaying them.
Assuming that you use MySQL (since you mentionned it in your question), let's start with a query that gives you the 10 most recent posts:
SELECT * FROM posts ORDER BY post_date DESC LIMIT 10
Then you can join this with the corresponding comments:
SELECT
p.*,
c.*
FROM
(SELECT * FROM posts ORDER BY post_date DESC LIMIT 10) p
INNER JOIN comments c ON c.post_id = p.id
Finally, let's set up a limit on the number of comments per posts. For this, you can use ROW_NUMBER() (available in MySQL 8.0) to rank the comments per post, and then filter only the a given number of comments. This gives you the 10 most recent posts along with each of their 3 most recents comments:
SELECT *
FROM (
SELECT
p.*,
c.*,
ROW_NUMBER() OVER(PARTITION BY p.post_id ORDER BY c.comment_date DESC) rn
FROM
(SELECT * FROM posts ORDER BY post_date DESC LIMIT 10) p
INNER JOIN comments c ON c.post_id = p.id
) x
WHERE rn <= 3
ORDER BY p.post_date DESC, c.comment_date DESC
Query results are ordered by post, then by comment date. So when your application fetches the resuts, you get 1 to 3 records per post, in sequence.
If you want the last 10 posts
SELECT p.post_id
FROM post p
ORDER BY p.publish_date DESC
LIMIT 10
Now if you want the comment of those posts:
SELECT c.comment_id, u.name
FROM comments c
JOIN users u
on c.user_id = u.user_id
WHERE c.post_id IN ( SELECT p.post_id
FROM post p
ORDER BY p.publish_date DESC
LIMIT 10 )
Now for the last 3 comments is where rdbms version is important so you can use row_number or not:
SELECT *
FROM (
SELECT c.comment_id, u.name,
row_number() over (partition by c.post_id order by c.comment_date DESC) as rn
FROM comments c
JOIN users u
on c.user_id = u.user_id
WHERE c.post_id IN ( SELECT p.post_id
FROM post p
ORDER BY p.publish_date DESC
LIMIT 10 )
) x
WHERE x.rn <= 3
You can do this in one query:
select . . . -- whatever columns you want here
from (select p.*
from posts p
order by <datecol> desc
fetch first 10 rows only
) p join
users u
on p.user_id = u.user_id join
comments c
on c.post_id = p.post_id;
This returns the posts/users/comments in one table, mixing the columns. But it only requires one query.

SQL: Count amount of comments for each post written by specific author

I always felt like I needed to improve my SQL knowledge, I am trying to, but still no success to find an answer to my question.
I have 2 tables: Post and Comments.
Post table has a PK PostID, Username, Title, Content.
Comments table has a PK CommentID, FK PostID, Username, Content.
Username in PostID is the Username of the post's author, in Comments it is commenter's username. Everything else should be pretty self explanatory.
Main challenge: I want to select all posts made by that specific author and display amount of comments for each post AND display zero if no comments are found. All in one query (if possible?)
So far the closest, I've gotten to was this:
SELECT p.*, COUNT(*) as CommentAmount
FROM Posts p
LEFT OUTER JOIN Comments c
ON p.PostID = c.PostID
WHERE p.Username = 'author1'
GROUP BY c.PostID
It works in a way, but doesn't display neither Post data, nor CommentAmount (0) if no comments exist for that PostID - which makes sense since it does not find any p.PostID that has the same c.PostID. How to make it display 0 CommentAmount as well? Is it even possible?
Here is the sample SQL Fiddle I've set up so you could test as well!: http://sqlfiddle.com/#!9/f8941
UPDATE: I made a little mistake in the schema... sorry guys. Fixed in the fiddle above^
UPDATE2: Thanks everyone for amazing answers! Funny how most solutions work perfectly on SQL Fiddle but seem not to work on my DB on the cloud, using MySQL Workbench... I'll have to look into that now then, thanks everyone!
How about this?:
SELECT p.*,
(SELECT COUNT(*) FROM Comments WHERE PostID=p.PostID) AS num_comments
FROM Posts p
WHERE p.Username = 'author1'
Make a small change to your query:
SELECT p.*, COUNT(c.CommentID) as CommentAmount
FROM Posts p
LEFT OUTER JOIN Comments c
ON p.PostID = c.PostID
WHERE p.Username = 'author1'
GROUP BY c.PostID
You only need to adjust your query by adding GROUP BY and changing COUNT() aggregate argument to point to Comments so that it will enable storing value 0 when there are no comments for a particular post.
It doesn't matter which column from Comments you will put inside COUNT(), since every column has a NULL value when JOIN condition is not met.
SELECT p.*, COUNT(c.CommentID) AS CommentAmount
FROM Posts p
LEFT JOIN Comments c
ON p.PostID = c.PostID
WHERE p.username = 'author1'
GROUP BY 1,2,3,4
Your only problem is your fiddle XD everything work fine u just have no author1 with post with no comment ! look i addded one http://sqlfiddle.com/#!9/9f2f6/1
SELECT p.*, (select COUNT(*) from comments c where c.PostID = p.PostID)
FROM Posts p
WHERE p.Username = 'author1'
You can try the following.
SQL Fiddle
SELECT kk.*, IFNULL(_aa.total, 0) AS total_comments
FROM Posts AS kk
LEFT JOIN (
SELECT aa.PostID, COUNT(*) AS total
FROM Posts AS aa
LEFT JOIN Comments AS bb
ON aa.PostID = bb.PostID
GROUP BY bb.PostID
) AS _aa
ON kk.PostID = _aa.PostID
WHERE kk.Username = 'author1'
Also check this and this about GROUP BY and nonaggregated columns in SELECT clause.

Posts, comments and advanced sql filtering

I have a database table of posts. I have another db table with comments, with a column for comment_id (ai) and a column for the post_id it's attached to.
I want to make a query list all of my posts once, sorted by last posted comment.
The problem is I can't figure out the sql query to do it. Whatever I try I get all the comments listed too. I'm very skilled or experienced with mysql, but I tried using different "joins". Is there any way to do it?
SELECT posts.*, comments.last_one
FROM posts
LEFT JOIN
(SELECT MAX(updated) as last_one, post_id
FROM comments
GROUP BY post_id
) as comments
ON comments.post_id = posts.id
ORDER BY comments.last_one DESC
SELECT distinct p.* FROM posts as p, comments as c
WHERE p.post_id = c.post_id ORDER BY c.time DESC
I just assumed your relational schema. You might need to adapt the query.
Alternative: create a view with the last_comment_time as a field.
CREATE VIEW posts AS
(SELECT *, (SELECT time FROM comments WHERE post_id = p.post_id ORDER BY time DESC LIMIT 1) as last_comment_time
FROM posts as p);

MySQL EXISTS and ORDER BY

I have two tables 'Comments' and 'Likes' and I can show all the comments that have been liked and order them by when the comments where added. What I can't seem to do at the moment is order the comments according to when they were liked.
This is what I have at the moment:
SELECT *
FROM comments AS c
WHERE EXISTS (
SELECT *
FROM likes AS l
WHERE c.commentID=l.commentID)
Could anyone help me with the SQL to show the comments in order with the one that was most recently liked first and so on...
Just to add - I only want to show the comment once and avoid showing any comments that have not been liked.
You want to join the tables.
SELECT comments.*
FROM comments JOIN likes ON comments.commentID = likes.commentID
GROUP BY comments.commentID
ORDER BY MAX(likes.date) DESC;
The JOIN makes rows with all of the fields from comments and likes. If you use LEFT JOIN it will include comments that have not been liked, but using a plain JOIN should do what you want.
The GROUP BY collapses rows so you only have one per comment.
The ORDER BY orders the rows by the like date. I used MAX(likes.date) because you will have potentially many like dates for each comment, and you want to choose a specific one. You could try MIN(likes.date) as well, depending on what you're looking for (most recently liked vs first liked).
If you have multiple likes for a given comment, then you need an aggregation, such as:
SELECT c.*
FROM comments c join
(select l.commentId, MIN(likedate) as FirstLikeDate, MAX(likedate) as MaxLikeDate
from likes l
group by l.commentId
) l
on c.commentId = l.CommentId
order by MaxLikeDate desc
Since you only want to show the comments that have like, you can do this:
SELECT * FROM comments INNER JOIN likes USING(commentID) ORDER BY like_date DESC;