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?!
Related
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
I am using MySQL 5.6 and I have a table structure like below
| user_id | email_1 | email_2 | email_3 |
| 1 | abc#test.com | | |
| 2 | xyz#test.com | | joe#test.com |
| 3 | | test#test.com | bob#joh.com |
| 4 | | | x#y.com |
I want to fetch the first n email addresses from this table.
For example, if I want to fetch the first 5 then only the first 3 rows should return.
This makes certain assumptions about the uniqueness of data, that might not be true...
SELECT DISTINCT x.* FROM my_table x
JOIN
(SELECT user_id, 1 email_id,email_1 email FROM my_table WHERE email_1 IS NOT NULL
UNION ALL
SELECT user_id, 2 email_id,email_2 email FROM my_table WHERE email_2 IS NOT NULL
UNION ALL
SELECT user_id, 3 email_id,email_3 email FROM my_table WHERE email_3 IS NOT NULL
ORDER BY user_id, email_id LIMIT 5
) y
ON y.user_id = x.user_id
AND CASE WHEN y.email_id = 1 THEN y.email = x.email_1
WHEN y.email_id = 2 THEN y.email = x.email_2
WHEN y.email_id = 3 THEN y.email = x.email_3
END;
You want to return as many rows as necessary to get five emails. So you need a running total of the email count.
select user_id, email_1, email_2, email_3
from
(
select
user_id, email_1, email_2, email_3,
coalesce(
sum((email_1 is not null) + (email_2 is not null) + (email_3 is not null))
over (order by user_id rows between unbounded preceding and 1 preceding)
, 0) as cnt_prev
from mytable
) counted
where cnt_prev < 5 -- take the row if the previous row has not reached the count of 5
order by user_id;
You need a current MySQL version for SUM OVER to work.
The counting of the emails uses a MySQL feature: true equals 1 and false equals 0 in MySQL. Thus (email_1 is not null) + (email_2 is not null) + (email_3 is not null) counts the emails in the row.
Demo: https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=ac415e71733699547196ae01cb1caf13
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 have a table named "message" that stores messages from one user to another user. I want to make a message box that contains both incoming and outcoming messages for particular user. This message box should be contain the last message between two users. So, I have to eliminate duplicate messages between two users. I tried group by and it eliminates duplicate messages but I don't pick the most recent message because order by works after group by. I tried distinct function to eliminates duplicate messages. It works well, but I have to select all colums which isn't possible with distinct
My message table:
+-------+---------+------+-----+------------+
| id | from_id | to_id| text| created_at |
+-------+---------+------+-----+------------+
| 1 | 1 | 2 | mes | 2014-01-16 |
| 2 | 2 | 1 | mes | 2014-01-17 |
| 3 | 1 | 3 | mes | 2014-01-18 |
| 4 | 3 | 1 | mes | 2014-01-19 |
+-------+---------+------+-----+------------+
My Group By SQL
SELECT * FROM message WHERE (from_id = 1 OR to_id = 1) GROUP BY(from_id + to_id) ORDER BY created_at DESC;
And Distinct
SELECT DISTINCT(from_id + to_id) FROM message WHERE (from_id = 1 OR to_id = 1)
In the above example, I want to select second and fourth message.
Is there a way to eliminate duplicate messages between two user from the result?
EDIT: I've improved the example
I tried group by and it eliminates duplicate messages but I don't pick the most recent message because order by works after group by
So you can order it before:
SELECT *
FROM (SELECT * FROM message ORDER BY created_at DESC)
WHERE (from_id = 1 OR to_id = 1) GROUP BY(from_id + to_id);
If I understand correctly what you're trying to achieve, you can leverage LEAST(), GREATEST() functions and non-standard GROUP BY extension behavior in MySQL like this
SELECT id, from_id, to_id, text, created_at
FROM
(
SELECT id, from_id, to_id, text, created_at
FROM message
ORDER BY LEAST(from_id, to_id), GREATEST(from_id, to_id), created_at DESC
) q
GROUP BY LEAST(from_id, to_id), GREATEST(from_id, to_id)
That will give you the last message row for each pair of users.
Output:
+------+---------+-------+------+------------+
| id | from_id | to_id | text | created_at |
+------+---------+-------+------+------------+
| 2 | 2 | 1 | mes | 2014-01-17 |
| 4 | 3 | 1 | mes | 2014-01-19 |
+------+---------+-------+------+------------+
Here is SQLFiddle demo
You can use:
ORDER BY id DESC LIMIT 1
or by timestamp (assuming it contains data AND time):
ORDER BY create_at DESC LIMIT 1
This will sort all results in a descending order and only give you the last row.
Hope this helps!
Just use a simple select. There is no reason for duplicates to be created.
SELECT from_id, to_id, text, created_at
FROM message
WHERE
(from_id = ? AND to_id = ??)
OR (from_id = ?? AND to_id = ?)
Here ? represents one id and ?? the other.
There would be no duplicates here. Ordering can be achieved in a few ways:
Order by most recent message regardless of sender:
SELECT from_id, to_id, text, created_at
FROM message
WHERE
(from_id = ? AND to_id = ??)
OR (from_id = ?? AND to_id = ?)
ORDER BY created_at DESC
Order all sender message first (then by created_at)
SELECT from_id, to_id, text, created_at
FROM message
WHERE
(from_id = ? AND to_id = ??)
OR (from_id = ?? AND to_id = ?)
ORDER BY from_id = ? DESC, created_at DESC
Try adding a HAVING clause after your GROUP BY: HAVING COUNT(*) > 1
or
SELECT
columns names, COUNT(*)
FROM
(SELECT DISTINCT
column names
FROM
message
)
message
GROUP BY
column names
HAVING COUNT(*) > 1
SQL Fiddle
MySQL 5.5.32 Schema Setup:
CREATE TABLE message
(`id` int, `from_id` int, `to_id` int, `text` varchar(3), `created_at` datetime)
;
INSERT INTO message
(`id`, `from_id`, `to_id`, `text`, `created_at`)
VALUES
(1, 1, 2, 'mes', '2014-01-16 00:00:00'),
(2, 2, 1, 'MUL', '2014-01-17 00:00:00')
;
Query 1:
SELECT *
FROM message
WHERE from_id = 1 OR to_id = 1
ORDER BY created_at DESC
limit 1
Results:
| ID | FROM_ID | TO_ID | TEXT | CREATED_AT |
|----|---------|-------|------|--------------------------------|
| 2 | 2 | 1 | MUL | January, 17 2014 00:00:00+0000 |
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 |
+----+----------+------------+---------+