I have a messaging system I am working on that receives messages from my Facebook Business Page and stores the information in my database. The information provided in the callback is a Sender ID, Recipient ID, and message data. I am wanting to group all messages between the sender and recipient together and return only the newest result (by row ID number) at the top of the list.
For example :
ID | Sender ID | Recipient ID | is_read
1 | 67890 | 12345 | 1
2 | 23232 | 12345 | 0
3 | 12345 | 67890 | 1
4 | 67890 | 12345 | 0
5 | 12345 | 23232 | 1
6 | 55555 | 12345 | 1
I don't want to show any results with Sender ID "12345".
The result I need should look something like this
Result | Row ID | Sender ID
1 | 4 | 67890
2 | 2 | 23232
3 | 6 | 55555
Here is my current query. Should return an Array with the newest message first no matter the senderid order. Currently, I get random results.
$sql = "SELECT id, senderid, sender_name, is_read FROM (SELECT id, senderid, sender_name, is_read FROM FB WHERE senderid != '".$fb_page_id."' GROUP BY senderid) AS f ORDER BY is_read ASC LIMIT ".$page_num * $perpage.", ".$perpage;
This has to be something simple.... just can't figure it out... lol.
If you just need the sender and its latest id in the resultset, we can just use aggregation here:
select max(id) as last_id, sender_id
from fb
where sender_id != 12345
group by sender_id
order by last_id desc
If, on the other hand, you need the entire latest row per sender, you can use window functions:
select *
from (
select fb.*, row_number() over(partition by sender_id order by id desc) rn
from fb
where sender_id != 12345
) f
where rn = 1
order by id desc
You can add the limit clause after the order by if that's needed.
In MySQL < 8.0, where window functions are not supported, we can use a correlated subquery instead:
select *
from fb f
where sender_id != 12345 and id = (
select max(f1.id) from fb f1 where f1.sender_id = f.sender_id
)
order by id desc
Related
So, what I'm trying to do is to retrieve a list of all "initial" messages a person sees in their messaging window
This is the table structure
thread_id | sender | receiver | message | date | sender_deleted | sender_received | read
xRdaQ | bTP5n | lCBNA | hello! | date | 0 | 0 |
xRdaQ | lCBNA | bTP5n | hey! | date | 0 | 0 |
1T4xR | bTP5n | An03R | hhi | date | 0 | 0 |
The queries I tried so far:
select * from messages where sender = 'bTP5n'
union select * from messages where receiver = 'bTP5n'
group by conversation_id
And I still get the two rows with the same thread_id
The same with this one query:
select * from messages where sender = 'bTP5n'
union select * from messages where receiver = 'bTP5n'
group by conversation_id order by date desc
Both of them are failing to return what I want, which is all unique thread_id where the sender or the receiver is equal to "bTP5n"
Disclaimer: Dummy data was used for this question
If you are using group by in second union query then it is only apply to the second query , if you want to apply in all the result then you have to write group by outside of all the results.
Try below query:
select * from
(select * from messages where sender = 'bTP5n'
union
select * from messages where receiver = 'bTP5n'
)
as a group by conversation_id order by date desc
The GROUP BY clause is required as soon as statistical calculation functions are used with raw data. It's not your case in your examples
I'm trying to return back all the messages for a user_id ( 1 ) in the table sorted by created_at desc, but have it grouped by sender_id or recipient_id depending on whichever created_at is newer.
messages
sender_id | recipient_id | text | created_at
1 | 2 | hey | 2017-03-26 04:00:00
1 | 2 | tees | 2017-03-26 00:00:00
2 | 1 | rrr | 2017-03-27 00:00:00
3 | 1 | edd | 2017-03-27 00:00:00
1 | 3 | cc3 | 2017-02-27 00:00:00
Ideally it would return
2 | 1 | rrr | 2017-03-27 00:00:00
1 | 3 | cc3 | 2017-02-27 00:00:00
The query I have so far is -
select *
from messages
where (
sender_id = 1
or recipient_id = 1
)
group by least(sender_id, recipient_id)
order by created_at desc
but it seems it is doing the order by before the group by.
Any help would be appreciative.
GROUP BY is intended for aggregation (sum, count, etc...), the fact that it orders is little more than an official side effect (that is being deprecated, and not guaranteed behavior in future versions). ORDER BY is done after GROUP BY, it sorts the final results, and can take multiple expressions. Your interchangeable use of the terms makes it difficult to understand exactly what you are looking for, but going by sample desired results this is probably it:
ORDER BY LEAST(sender_id, recipient_id), created_at DESC
However, since all rows in your sample result have the same "LEAST" value, it could be this:
ORDER BY LEAST(sender_id, recipient_id), created_at DESC
If you are attempting to get the most recent post by user 1 where they are a sender and the most recent post by user 1 where they are a recipient.
I would do a union with the first query getting the most recent post as a sender and the second getting the most recent post as a recipient.
select *
from messages
join
(
select
sender_id,
max(created_at) as max1
from messages
where
sender_id = 1
group by
sender_id
) t1
on messages.created_at = t1.max1
union
select *
from messages
join
(
select
recipient_id,
max(created_at) as max2
from messages
where
recipient_id = 1
group by
recipient_id
) t2
on messages.created_at = t2.max2
I want to retrieve messages and number of unread message (0) for a sender and dest in a conversation.
+---------------------------------------------------------------+
| messages |
+---------------------------------------------------------------+
| message_id | id_sender | id_dest | subject | message | read |
+---------------------------------------------------------------+
| 1 | 25 | 50 | Hi | message | 0 |
| 2 | 25 | 50 | Hi2 |message2 | 1 |
| 3 | 25 | 50 | Hi3 |message3 | 0 |
+---------------------------------------------------------------+
In this case the result must be 2. I try with
SELECT *
FROM
(SELECT message,sum(read = 0) as nm_messages
FROM messages
WHERE ( id_sender = id1 AND id_dest = id2 ) or
( id_dest = id1 AND id_sender = id2 )
ORDER BY message_id DESC
LIMIT 10) AS ttbl
ORDER BY message_id ASC
The messages part is ok but when
I add
sum(read = 0) as nm_messages
return only the firsth message if possible for both mysql postgresql
Thanks!
I have used PostgreSQL 9.4.11, compiled by Visual C++ build 1800, 64-bit.
With distinct on you can eliminate same rows with their unique ids. in this case i have used id_sender.
SELECT DISTINCT ON ( expression [, ...] ) keeps only the first row of each set of rows where the given expressions evaluate to equal
more information look at this link:
distinct on
Below sql query will return only the first message and the number of unread messages (0):
SELECT distinct on (id_Sender)
message,
count(case when read=0 then 1 end) over() as nm_messages
FROM messages
group by id_Sender,message,message_id
order by id_Sender,message_id
message | nm_message
message | 2
You should use the sum with if condition. Should be like this:
SELECT *
FROM
(SELECT GROUP_CONCAT(message),sum(IF(read = 0,1,0)) as nm_messages
FROM messages
WHERE ( id_sender = id1 AND id_dest = id2 ) or
( id_dest = id1 AND id_sender = id2 )
GROUP BY id_sender, id_dest
LIMIT 10) AS ttbl
ORDER BY message_id ASC
When the condition is true (read = 1), then it will sum up 1, otherwise 0.
I just saw that there was no grouping in the query. I added that. Also if you use an aggregate function, it doesnt make sense to do that only for one field (read), and not for others (message). So i put group_concact around message. That makes more sense to me?!
While making a game the MySQL call to get the top 10 is as follows:
SELECT username, score FROM userinfo ORDER BY score DESC LIMIT 10
This seems to work decently enough, but when paired with a call to get a individual player's rank the numbers may be different if the player has a tied score with other players. The call to get the players rank is as follows:
SELECT (SELECT COUNT(*) FROM userinfo ui WHERE (ui.score, ui.username) >= (uo.score, uo.username)) AS rank FROM userinfo uo WHERE username='boddie';
Example results from first call:
+------------+-------+
| username | score |
+------------+-------+
| admin | 4878 |
| test3 | 3456 |
| char | 785 |
| test2 | 456 |
| test1 | 253 |
| test4 | 78 |
| test7 | 0 |
| boddie | 0 |
| Lz | 0 |
| char1 | 0 |
+------------+-------+
Example results from second call
+------+
| rank |
+------+
| 10 |
+------+
As can be seen, the first call ranks the player at number 8 on the list, but the second call puts him at number 10. What changes or what can I do to make these calls give matching results?
Thank you in advance for any help!
You need in the first query to :
ORDER BY
score DESC,
username DESC
This way it will reach at rank 10... this is due to the username comparison in the second query :
(ui.score, ui.username) >= (uo.score, uo.username)
I would change your Order by to include username, so that you always get the same order. So it would look like:
... ORDER BY score DESC, username ASC ...
Another way of doing it:-
SELECT UsersRank
FROM
(
SELECT userinfo.score, userinfo.username, #Rank:=#Rank+1 AS UsersRank
FROM userinfo
CROSS JOIN (SELECT (#Rank:=0)) Sub1
ORDER BY userinfo.score DESC, userinfo.username
) Sub2
WHERE username = 'boddie'
SELECT
uo.username,
(SELECT COUNT(*)+1 FROM userinfo ui WHERE ui.score>uo.score) AS rank
FROM userinfo uo
WHERE uo.username='boddie'
Or if you need to get username, score and ranks:
SELECT
uo.username,
uo.score,
(#row := #row + 1) AS rank
FROM userinfo uo
JOIN (SELECT #row := 0) r
ORDER BY uo.score DESC, uo.username ASC
You could add
HAVING uo.username = 'boddie'
to get only one user
Try this.
SELECT (COUNT(*)+1) AS rank
FROM userinfo ui
WHERE ui.score > (SELECT score
FROM userinfo
WHERE username='boddie');
The following SELECT statement
select *
from messages
where receiverID = '5'
group BY senderID
order by id DESC
database:
id | senderID | receiverID | message
1 | 245 | 5 | test 1
2 | 89 | 5 | test 2
3 | 79 | 5 | test 3
4 | 245 | 5 | test 4
5 | 245 | 5 | test 5
For senderID=245 I expected to return the row with id=5 , but it dosent it returns row with id=1, but i want the last row. How to achieve that ?
returns:
id | senderID | receiverID | message
1 | 245 | 5 | test 1
2 | 89 | 5 | test 2
3 | 79 | 5 | test 3
Ohh I made it :D
so this is the code that worked,for anyone with similar question
SELECT * FROM ( SELECT * FROM messages WHERE
receiverID = '5' ORDER BY id DESC) AS m GROUP BY senderID ORDER BY id DESC
This is not possible. You have to do something like:
[...] WHERE `id` = (SELECT MAX(`id`) FROM `messages` WHERE `receiverID` = '5')
Personally I'd consider a subquery, something along the lines of this should do the job for you
SELECT messagesOrdered.*
FROM (
SELECT *
FROM messages
WHERE receiverID = '5'
ORDER BY id DESC
) AS messagesOrdered
GROUP BY senderID
You may wish to check what keys you have set up depending on how large the table is.
The problem with using MAX is that if you use MAX on the id field then it will get the number you are looking for, however using MAX on another field does not get the data that matches that id. Using the subquery method, the inner query is doing the sorting and then the GROUP on the outside will group based on the order of rows in the inner query.
SELECT * FROM messages m
JOIN
( SELECT senderID, MAX(id) AS last
FROM messages
WHERE receiverID = '5'
GROUP BY senderID ) mg
ON m.id = mg.last
Not sure I understand your question completely, but it sounds to me like you want:
select max(id),
senderId,
max(receiverId),
max(message)
from messages
where receiverID = '5'
group BY senderID
order by id DESC
Note that you need to include message into your aggregate as well, otherwise you'll get unpredicatable results (other DBMS wouldn't allow leaving out the max(message) but MySQL will simply return a random row from the group).
Here it goes mine :)
select m1.* from messages m1
left join messages m2
on m1.senderid = m2.senderid and m1.id < m2.id
where m2.id is null and receiverID = '5'
Given your example this would return:
+----+----------+------------+---------+
| ID | SENDERID | RECEIVERID | MESSAGE |
+----+----------+------------+---------+
| 2 | 89 | 5 | test 2 |
| 3 | 79 | 5 | test 3 |
| 5 | 245 | 5 | test 5 |
+----+----------+------------+---------+