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
Related
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).
I have two tables in my database. One of which is a table called players and the other one is bans:
players table: ID, Score
bans table: user_id, reason
What I need to do is: select players that where reason = cheating and Score < 250.
I tried doing some JOINS when I tried FULL JOIN I found that thing doesn't exist in MySQL so any help would be good, thanks in advance!
I also tried it like this but I always get zero rows
SELECT bans.user_id, players.ID
FROM bans
INNER JOIN players ON bans.user_id=players.ID;
BANS TABLE
user_id reason
133032 swearing
133040 name not allowed
PLAYERS TABLE
id score
15 13378
21 215216
133032 15
133040 157
You query is a good start. For your sample data, it will give you results. To meet your requirement, you just need a few additional filters:
select b.user_id, p.id
from bans b
inner join players p
on p.id = b.user_id
and p.score < 250
where b.reason = 'cheating'
My sql's rusty. I need to select the 30 most recent messages (those with the 30 largest id's), and then use the sender_id to join with the users table id.
In English, I want the users who sent the last 30 messages.
Here my query (which doesn't run):
SELECT * FROM group_messages
WHERE group_id=52
ORDER BY id DESC
LIMIT 30
LEFT JOIN users
ON users.id=group_messages.sender_id
If there's a better approach to this kind of query, then by all means.
Note: The first part works in selecting the 30 most recent messages. The trouble came when I tried joining on the users table. (And I just realized even if this query did run, I would need to add GROUP BY users.id a user may have sent more than 1 of the 30 most recent messages.
The JOIN clause has to come before WHERE, ORDER BY, and LIMIT
SELECT DISTINCT id
FROM (
SELECT u.id
FROM group_messages AS g
INNER JOIN users AS u on u.id = g.sender_id
WHERE group_id = 52
ORDER BY id DESC
LIMIT 30) AS x
I put it in a subquery so I could then perform the DISTINCT selection. If you do that in the same query, it will get rid of the duplicates before selecting the most recent 30 rows, so you'll get the 30 most recent senders, not the senders of the 30 most recent messages.
I doubt you really need a LEFT JOIN instead of INNER JOIN. That would only be needed if a message could have a sender ID that isn't in users.
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 :)