This question already has answers here:
SQL select only rows with max value on a column [duplicate]
(27 answers)
Closed 2 years ago.
I have database to store customer and messages
I am trying to get list of all the customer and their latest messages like first screen in messenger.
SELECT *
FROM message AS m
LEFT JOIN customer AS c ON c.id=m.sender_id
ORDER BY m.sent_at DESC
but this returns all the message for all user. I've also tried doing this
SELECT *
FROM message AS m
LEFT JOIN customer AS c ON c.id=m.sender_id
GROUP BY c.id
but this doesn't run on all databases and cannot sort result set to get latest messages only.
One option uses row_number(), available in MySQL 8.0:
select * -- better enumerate the columns you want here
from customer as c
left join (
select m.*, row_number() over(partition by m.sender_id order by sent_at desc) rn
from messages m
) m on on c.id = m.sender_id and m.rn = 1
order by m.sent_at desc
This gives you the last message per customer. You can change the condition on rn if you want more messages (rn <= 3 would give you three messages per customer).
Note that I changed the order of the tables in the left join, so it allows customers without messages (rather than messages without customers, which probably does not make sense).
If you are running an earlier version, than an alternative is to filter with a subquery:
select * -- better enumerate the columns you want here
from customer as c
left join messages m
on m.sender_id = c.id
and sent_at = (select min(m1.sent_at) from messages m1 where m1.sender_id = m.sender_id)
For perforamnce with the correlated subquery, consider an index on (sender_id, sent_at) (ideally, there should be no duplicates in these columns).
Related
This question already has answers here:
Ordering a MySQL result set by a MAX() value of another table
(2 answers)
Closed 4 years ago.
I have two tables - groups and messages.
Messages has the following fields group_id and date_created. So a lot of messages can be added to a single group. I want to select all groups from table - most relevant on top, i.e. order by latest message date. I've tried something like this
SELECT g.*, MAX(m.date_created) AS mdt FROM groups g
LEFT JOIN messages m ON g.id = m.group_id
ORDER BY mdt DESC;
But this query returns only one row and max message date from the whole table.
You are missing group by:
SELECT g.*, MAX(m.date_created) AS mdt
FROM groups g LEFT JOIN
messages m
ON g.id = m.group_id
GROUP BY g.id
ORDER BY mdt DESC;
i am only starting to play with MySQL and i seem to have been stumped by one problem.
I have 2 tables that I have left joined them with the part of working but it's not enough. I want them be order by id in one table by top and down this table messages
Example
table chat
id
1
2
3
table messages
chat_id
1
2
3
SELECT *
FROM (SELECT * FROM `messages` ) AS m LEFT JOIN
chat as c
ON m.chat_id = c.id
WHERE c.one ='$user_id' OR c.two = '$user_id'
GROUP BY c.id, m.chat_id
ORDER BY m.dates DESC
LIMIT 8
Are you trying to get a list of chats with the most recent message from each chat?
If so, your best bet is to add a column to the chat table and include a 'latest_message_id' so you can query like this:
SELECT * FROM chat LEFT JOIN messages ON messages.id = chat.latest_message_id
Otherwise you will need to run a really big subquery which will waste lots of CPU and memory
I'm building a Chatapplication that's a bit like the facebookchat. I have users,conversations and messages. All 3 have their own tables. For now I try to get all converstations containing a certain user and the latest message of the conversation.
I tried this query, but in a fact I only get 1 row back, but there are more rows matching
SELECT conversations.id as converid,
messages.from as messageauthor,
messages.message as message
FROM conversations INNER JOIN (SELECT * FROM messages
ORDER BY date DESC LIMIT 1) as messages
ON messages.conversationid=conversations.id
WHERE user1=3
OR user2=3
When I do i.e.
SELECT conversations.id as converid,
messages.from as messageauthor
FROM conversations INNER JOIN messages
ON messages.conversationid=conversations.id
WHERE user1=3
OR user2=3
I get all results, for sure, and when I check the converid's I get 3 unique Id's, so at least there are 3 converstations going on with userid 3. So the top query should also return 3. Now I don't understand why it only returns 1 row. Does the limit 1 from the nested query affect the whole query?
Looking forward for some pointers...
No. The limit 1 affects the subquery, so it is only returning one row. So, there is only one match.
What is the issue with this query (your second query, but formatted differently):
SELECT c.id as converid, m.from as messageauthor
FROM conversations c INNER JOIN
messages m
ON m.conversationid=c.id
WHERE user1=3 OR user2=3;
I see, you want the latest message. Try calculating it and joining back in:
SELECT c.id as converid, m.from as messageauthor
FROM conversations c INNER JOIN
messages m
ON m.conversationid=c.id join
(select m.conversationid, max(date) as maxdate
from messages m
group by m.conversationid
) mmax
on mmax.conversationid = m.conversationid and m.date = mmax.maxdate
WHERE user1=3 OR user2=3;
Context:
I have an app that shows posts and comments on the home page.
My intention is to limit the number of posts shown (ie, 10 posts) and...
Limit the number of comments shown per post (ie, 2 comments).
Show the total number of comments in the front end (ie, "read all 10 comments")
MySQL:
(SELECT *
FROM (SELECT *
FROM post
ORDER BY post_timestamp DESC
LIMIT 0, 10) AS p
JOIN user_profiles
ON user_id = p.post_author_id
LEFT JOIN (SELECT *
FROM data
JOIN pts
ON pts_id = pts_id_fk) AS d
ON d.data_id = p.data_id_fk
LEFT JOIN (SELECT *
FROM comment
JOIN user_profiles
ON user_id = comment_author_id
ORDER BY comment_id ASC) AS c
ON p.post_id = c.post_id_fk))
I've failed to insert LIMIT and COUNT in this code to get what I want - any suggestions? - will be glad to post more info if needed.
If I'm understanding you correctly you want no more than 10 posts (and 2 comments) to come back for each unique user in the returned result set.
This is very easy in SQLServer / Oracle / Postgre using a "row_number() PARTITION BY".
Unfortunately there is no such function in MySql. Similar question has been asked here:
ROW_NUMBER() in MySQL
I'm sorry I can't offer a more specific solution for MySql. Definitely further research "row number partition by" equivalents for MySql.
The essence of what this does:
You can add a set of columns that make up a unique set, say user id for example sake (this is the "partition") A "row number" column is then added to each row that matches the partition and starts over when it changes.
This should illustrate:
user_id row_number
1 1
1 2
1 3
2 1
2 2
You can then add an outer query that says: select where row_number <= 10, which can be used in your case to limit to no more than 10 posts. Using the max row_number for that user to determine the "read all 10 comments" part.
Good luck!
This is the skeleton of the query you're looking for:
select * from (
select p1.id from posts p1
join posts p2 on p1.id <= p2.id
group by p1.id
having count(*) <= 3
order by p1.post_timestamp desc
) p left join (
select c1.id, c2.post_id from comments c1
join comments c2 on c1.id <= c2.id and c1.post_id = c2.post_id
group by c1.id
having count(*) <= 2
order by c1.comment_timestamp desc
) c
on p.id = c.post_id
It will get posts ordered by their descending timestamp but only the top 3 of them. That result will be joined with the top 2 comments of each post order by their descending timestamp. Just change the column names and it will work :)
I have a mysql DB with two tables:
messages
message_answers
I'd like to fetch all messages and the number of answers for each of them, like:
first message (10 answers)
second message (5 answers)
Is it possible with a single sql query ?
I tried a query and a subquery for the count, but I don't know how to have the current id from the main query to make the "WHERE" restriction on the subquery (like: "message_answers.message_id = messages.id").
Thank you,
Sébastien
SELECT m.message, COUNT(ma.answer_id) AS AnswerCount
FROM messages m
LEFT JOIN message_answers ma
ON m.id = ma.message_id
GROUP BY m.message
select m.id, count(ma.id)
from messages m
join join message_answer ma on ma.id = m.message_id
group by 1