MySQL EXISTS and ORDER BY - mysql

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;

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.

use COUNT(*) values from one table to another

Suppose I have two tables, users and posts. Posts has the following fields, userid, postid, etc and userid can appear multiple times as one user can write multiple posts....I'm just trying sort the users table based off the # of occurrences per userid in the posts table. I can get the # of occurrences per user using this
SELECT userid, COUNT(*)
FROM posts
GROUP BY userid;
I would like to use the values under COUNT(*) column, maybe add it to my other table because then I can simply to something like this
SELECT * FROM users
ORDER BY newcolumn ASC;
but I'm having trouble doing that. Or can I do it without having to add an extra column? Hints please. Thanks
Left join is the key here!
SELECT users.userid,count(posts.userid) AS total_count
FROM users
LEFT JOIN posts on posts.userid = users.userid
GROUP BY users.userid
ORDER BY total_count DESC;
We are taking the left join on two tables with same user_id and we are counting the total number of posts per user using group by. Finally sort by count and show results.
try an left join:
select users.userid, [user fields],count(postid) as posts_count
from users
left join posts on posts.userid = users.userid
group by users.userid,[user fields]
order by posts_count desc.
You want to select users (FROM users) but you want to sort based on criteria in another table (COUNT(*) FROM posts) -- therefore you need to use a JOIN
Off-hand I can't seem to recall if "JOIN" or "RIGHT JOIN" or "FULL JOIN" is what you need if you wanted to get a cartesian product of the tables then group and aggregate on a single field, but I can avoid the need to remember with a subquery (hopefully someone will soon post a smaller and smarter answer):
SELECT users.* FROM users
JOIN (
SELECT userid, COUNT(*) as count
FROM posts
GROUP BY userid
) as subquery ON users.id = subquery.userid
ORDER BY subquery.count
Note: I haven't tested this query, but it looks good to me. Again: hopefully someone will post a better answer soon as I'm not doing my due dilligence, but you definitely need a JOIN :)
You could add a post_count column to the users table, but you would also have to update that count column every time a user creates a new post and you would have to build that logic into your application.
Otherwise, it looks like the answer from FallAndLearn will get you what you need.

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

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.

Display only best result out of one-to-many select

I'm having trouble getting my head around how to handle something I've thought of in SQL. I have bookmarks, and comments that the users posted about them. I'm using a single table for all comments. So we have a one-to-many relationship between the bookmarks and the comments.
There is another table, acting as the middle-man, linking each bookmark to all its comments.
There are two types of comments. Suggested titles for the bookmark, and general comments. Suggested titles have both a title and a description, while general comments have only a description. There's also a rating system for the suggested titles, so that the home page can pick the top-rated title for each bookmark to display.
So, main things to make clear. There's the Bookmarks table with BID and URL, and also the Comments table with CID, Title, Comment, and Rating. The BooksNComms is the connecting table between them.
SELECT comments.title, comments.comment
FROM comments
INNER JOIN booksncomms ON comments.cid=booksncomms.cid
WHERE booksncomms.bid=1
AND comments.title is not null
ORDER BY comments.rating
LIMIT 0, 1;
The above works in getting the best Title and Description (Comment) for a certain BID. What I want to do is make the above work for, say, the 10 newest bookmarks.
SELECT bookmarks.url, comments.title, comments.`comment`, comments.rating
FROM bookmarks
INNER JOIN booksncomms
ON bookmarks.bid=booksncomms.bid
INNER JOIN comments
ON comments.cid=booksncomms.cid
JOIN (
SELECT bookmarks.bid
FROM bookmarks
ORDER BY bookmarks.datecreated DESC
LIMIT 1
)
AS a
ON a.bid=bookmarks.bid
WHERE comments.title IS NOT NULL
ORDER BY bookmarks.url;
The above gives me all titles for the 10 newest bookmarks.
Is there a way I can select only the highest rated title for each of the 10 newest bookmarks?
(OP's own solution, split off from the question)
#LefterisAslanoglou says:
I realized I knew the answer just a few minutes after I posted the question here. It's been bugging me for hours, but it was a simple matter of getting a table that has the best rated title for each bookmark and then joining that to the one with all the titles for the latest bookmarks.
SELECT bookmarks.url, comments.title, comments.comment, comments.rating
FROM bookmarks
INNER JOIN booksncomms ON bookmarks.bid=booksncomms.bid
INNER JOIN comments ON comments.cid=booksncomms.cid
JOIN (
SELECT bookmarks.bid
FROM bookmarks
ORDER BY bookmarks.datecreated DESC
LIMIT 10
) AS a ON a.bid=bookmarks.bid
JOIN (
SELECT comments.cid, MAX(comments.rating)
AS maxrating
FROM comments
INNER JOIN booksncomms ON comments.cid=booksncomms.cid
GROUP BY booksncomms.bid
) AS b ON b.cid=comments.cid
WHERE comments.title IS NOT NULL
ORDER BY bookmarks.datecreated DESC;
Try
SELECT bookmarks.url, comments.title, comments.`comment`, MAX(comments.rating) rating
FROM bookmarks
INNER JOIN booksncomms
ON bookmarks.bid = booksncomms.bid
INNER JOIN comments
ON comments.cid = booksncomms.cid
WHERE comments.title IS NOT NULL
ORDER BY bookmarks.datecreated DESC, bookmarks.url
LIMIT 10