MySQL sort by grouped date in joined table - mysql

I have a table, called Posts, and a second table, called Comments. These are linked by an id column in the posts table, and a postid column in the comments table.
Both tables have a date column, which is the date when they were posted. I want to be able to sort my posts based on the newest activity, so they should be sorted by the post's date (if there are no comments) or the newest comment's date.
In order to do this, I've constructed this simple query:
SELECT Posts.id FROM Posts
INNER JOIN Comments ON Posts.id = Comments.postid
ORDER BY Comments.date ASC
Unfortunately, this has a very obvious problem. If there are no comments on a post, it will be ignored. If there are multiple comments on a post, it will show up multiple times in the results.
How can I construct a query to satisfy these requirements?

You need to use a LEFT JOIN, that returns all rows from Posts, and rows from Comments only when the JOIN succeedes. If the join doesn't succeed because there are no comments with comments.postid=posts.id, you still get all values from Posts, but values from Comments will be Null.
You then have to use GROUP BY, so you will get only one row for each ID, and you can use MAX() aggregate function to get the maximum date in comments table.
If there are no comments, max(comments.date) will be Null, so COALESCE will return Posts.date instead.
And the final query is this:
SELECT Posts.id
FROM Posts LEFT JOIN Comments
ON Posts.id = Comments.postid
GROUP BY Posts.id
ORDER BY coalesce(max(Comments.date),Posts.date) ASC

You need a left outer join to take into account that there may be no comments:
SELECT Posts.id
FROM Posts left JOIN
Comments
ON Posts.id = Comments.postid
group by posts.id
ORDER BY coalesce(max(Comments.date), posts.date) ASC

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;

Can you use Count(*) in an inner join?

Currently I have the following database:
Table 1: Customer_Stores
unique_id
page_address
date_added
guide_summary
user_name
cover_photo
guide_title
Table 2: Customer_Stories_Likes
story_id
likex
The 'like' column in the second table contains a 1 or a 0 to indict whether or not a user has liked a post.
What I'd like to do is join these two tables together with 'post_id' and count all of the 'likes' for all the posts based on post_id and order these by how many likes each post got. Is this possible with a single statement? or is it better to use a Count(*) to first determine how many likes each post has?
Yes, it's possible, but you don't need an inner join, because you don't actually need the posts table to do it.
SELECT post_id, count(like) AS post_likes
FROM likes
WHERE like = 1
GROUP BY post_id
ORDER BY post_likes DESC
If you need other information from the posts table as well, you could join it to a subquery that gets the like counts.
SELECT posts.*, like_count
FROM
posts LEFT JOIN
(SELECT post_id, count(like) AS like_count
FROM likes
WHERE like = 1
GROUP BY post_id) AS post_likes
ON posts.post_id = post_likes.post_id
ORDER BY like_count DESC
I used LEFT JOIN rather than INNER JOIN, you can use INNER JOIN if you don't want to include posts with no likes.

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);

How to write mysql subselect properly with conditions and limiting

I have three Tables:
Posts:
id, title, authorId, text
authors:
id, name, country
Comments:
id, authorId, text, postId
I want to run a mysql command which selects the first 5 posts which were written by authors, whose country is 'Ireland'. In the same call, I want to retrieve all the comments for those five posts, and also the author info.
I've tried the following:
SELECT posts.id as 'posts.id', posts.title as 'posts.title' (etc. etc. list all fields in three table)
FROM
(SELECT * FROM posts, authors WHERE authors.country = 'ireland' AND authors.id = posts.authorId LIMIT 0, 5 ) as posts
LEFT JOIN
comments ON comments.postId = posts.id,
authors
WHERE
authors.id = posts.authorId
I had to include every field with an alias ^ because there was a duplicate for id, and more fields in future may become duplicates as I'm looking for a generic solution.
My two questions are:
1) I am getting a duplicate field entry from within my subselect for id, so do I have to list out all my fields as aliases again within the subselect or is there only one field I need for a subselect
2) Is there a way to auto-alias my call? At the moment I've just aliased every field in the main select but can it do this for me so there are no duplicates?
Sorry if this isn't very clear it's a bit of a messy problem! Thanks.
You are doing an unnecessary join back to the author table in your query. You get all the fields you want in the posts subquery. I would rename this to something other than an existing table, perhaps pa to indicate posts and authors.
You say you want the first 5 posts, but have no order clause. A better form of the query is:
SELECT pa.id as 'posts.id', pa.title as 'posts.title' (etc. etc. list all fields in three table)
FROM (SELECT *
FROM posts join
authors
on authors.id = posts.authorId
WHERE authors.country = 'ireland'
order by post.date
LIMIT 0, 5
) pa LEFT JOIN
comments c
ON c.postId = pa.id
Note that this returns the first five posts and their authors (as specified in the question). But one author may be responsible for all five posts.
In MySQL, you can use * and it will get rid of duplicate aliases in the from clause. I think this is dangerous. It is better to list all the columns you want.
To answer your questions:
You can select as many (or as few) columns as you need from a sub-query
You do not need to join the authors table again since you already selected all fields in the sub-query (and so get rid of duplicate columns names).
A few additional remarks...
... about the JOIN syntax
Prefer the form
FROM t1 JOIN t2 ON (t1.fk = t2.pk)
to the obsolete, obscure
FROM t1, t2 WHERE t1.fk = t2.pk
... about the use of a LIMIT clause without an ORDER BY clause
The order in which rows are returned by a SELECT statement without an ORDER BY clause is undefined. Therefore, a LIMIT n clause without an ORDER BY clause could return any n rows in theory.
Your final query should look like this:
SELECT *
FROM (
SELECT *
FROM posts
JOIN authors ON (authors.id = posts.authorId )
WHERE authors.country = 'ireland'
ORDER BY posts.id DESC -- assuming this column is monotonically increasing
LIMIT 5
) AS last_posts
LEFT JOIN comments ON ( comments.postId = last_posts .id )

How to left join multiple one to many tables in mysql?

i have a problem with joining three tables in mysql.
lets say we have a table named posts which I keep my entries in it, i have a table named likes which i store user_id's and post_id's in and a third table named comments which i store user_id's and post_id's and comment's text in it.
I need a query that fetches list of my entries, with number of likes and comments for each entry.
Im using this query:
SELECT posts.id, count(comments.id) as total_comments, count(likes.id) as total_likes
FROM `posts`
LEFT OUTER JOIN comments ON comments.post_id = posts.id
LEFT OUTER JOIN likes ON likes.post_id = posts.id
GROUP BY posts.id
but there is a problem with this query, if comments are empty for an item, likes count is just ok, but lets say if an entry has 2 comments and 4 likes, both total_comments and total_likes will be "8", meaning that mysql multiplies them.
I'm confused and I dont know what whould I do.
Thanks in advace.
Use count(distinct comments.id) and count(distinct likes.id), provided these ids are unique.
Well this is one way to approach it (assuming mysql allows derived tables):
SELECT posts.id, comments.total_comments, likes.total_likes
FROM `posts`
LEFT OUTER JOIN (select post_id, count(id) as total_comments from comments) comments
ON comments.post_id = posts.id
LEFT OUTER JOIN (select post_id, count(id) as total_likes from likes) likes
ON likes.post_id = posts.id
You could also use correlated subqueries. You may want a case statment inthere to account for putting in a 0 when there are no matched records.
Let's try a correlated subquery:
SELECT posts.id,
(select count(Id) from comments where post_id = posts.id) as total_comments,
(select count(Id) from likes where post_id = posts.id) as total_likes
FROM `posts`