ordering by another table column mysql - mysql

I have two tables, messages and thread messages is the main table and thread is where I keep the conversation between two users. I want to select only from messages table and order by thread_id from thread table. I am using the below sql but it doesn't order as I want it.
My aim is when new message is inserted to thread table make the parent message in messages table appear at the top. Does anyone have any idea?
SELECT m.*
FROM messages AS m
JOIN thread AS t
ON t.thread_id = m.id
WHERE ( m.to_user = ?
AND m.deleted != ?
AND m.del2 != ?)
OR
(m.from_user = ?
AND m.conversation = 'true'
AND m.deleted != ?
AND m.del2 != ?)
ORDER BY t.thread_id desc);
messages table
id to_user from_user message is_read deleted del2 conversation
----------------------------------------------------------------------------------------
1 user1 user2 hi mark, true true
2 user3 user4 wass up yo? true true
3 user1 user3 blah blah
thread table
thread_id thread_to_user thread_from_user thread_message thread_message_id thread_is_read
---------------------------------------------------------------------------------------------------
1 user2 user1 hi there, 1 1
2 user1 user2 hey, wassup 1 1
3 user2 user1 not much, hw u doin 1 1
4 user1 user2 doing great and you? 1 0
5 user3 user4 heyyyy 2 1
6 user4 user3 hi, u coming? 2 0
What I am expecting when echoing the row:
id to_user from_user message is_read deleted del2 conversation
----------------------------------------------------------------------------------------
2 user3 user4 wass up yo? true true
1 user1 user2 hi mark, true true
3 user1 user3 blah blah

Try this one:
SELECT DISTINCT(id), to_user,from_user,
message, is_read, conversation
FROM messages
LEFT JOIN (
SELECT thread_id, thread_message_id
FROM messages m
LEFT JOIN thread t
ON m.id = t.thread_message_id
) tbl
ON messages.id = tbl.thread_message_id
ORDER BY tbl.thread_id DESC
See fiddle demo
You could put your WHERE inside the subquery, like:
SELECT DISTINCT(id), to_user,from_user,
message, is_read, conversation
FROM messages
LEFT JOIN (
SELECT thread_id, thread_message_id
FROM messages m
LEFT JOIN thread t
ON m.id = t.thread_message_id
WHERE ( m.to_user = ?
AND m.deleted != ?
AND m.del2 != ?)
OR
(m.from_user = ?
AND m.conversation = true
AND m.deleted != ?
AND m.del2 != ?)
) tbl
ON messages.id = tbl.thread_message_id
ORDER BY tbl.thread_id DESC
See Fiddle Demo with WHERE
Update (Using MAX() with subquery)
SELECT id, to_user, from_user, message, is_read, conversation
FROM (
SELECT m.id, max(t.thread_id) thread_id, m.to_user,m.from_user,m.message, m.is_read, m.conversation
FROM messages m
LEFT JOIN thread t on(t.thread_message_id = m.id)
WHERE ( m.to_user = 'user1'
AND m.deleted != true
AND m.del2 != true)
OR
(m.from_user = 'user3'
AND m.conversation = true
AND m.deleted != true
AND m.del2 != true)
GROUP BY m.id
ORDER BY thread_id desc
) tbl
See Other Demo

please refer the below query and the fiddle example
select m.id, max(t.thread_id) thread_id, m.to_user,m.from_user,m.message, m.is_read, m.conversation
from messages m
left join thread t on(t.thread_message_id = m.id)
group by t.thread_message_id
order by thread_id desc;
fiddle example

Related

MySQL: Select newest record from table for every user I talked to

I have 2 tables.
users schema:
private_messages schema:
time_sent is a datetime. All other fields are varchars and ints.
sender_id and receiver_id both refer to id in users.
What I want:
Given a users id, say 1, select the last sent message for every other user (whether the given user sent it or the other user sent it). It should return all the columns of the private_messages table along with the other user's username, and ordered by the time_sent.
The functionality I'm trying to achieve is that of when you open up all of your text messages, and you can see a preview of the last message sent between you and everyone else. This query is killing me because of the fact that the given users id can appear in either the sender_id or the receiver_id. Please help me out with writing the query.
Here is exported sql code to create the tables and their contents (just in case you want something to test with):
users
private_messages
private_messages 2
This should do the trick
SELECT U.username,
U2.username,
IF(PM.sender_id = U.id, 'Sent', 'Received') AS `action`,
PM.message,
PM.time_sent
FROM users U
INNER JOIN private_messages PM ON (PM.sender_id = U.id OR PM.receiver_id = U.id)
INNER JOIN users U2 ON (U2.id = IF(PM.sender_id = U.id, PM.receiver_id, PM.sender_id))
WHERE NOT EXISTS (
SELECT *
FROM private_messages NXT_PM
WHERE NXT_PM.time_sent > PM.time_sent
AND (
( NXT_PM.sender_id = PM.sender_id AND NXT_PM.receiver_id = PM.receiver_id )
OR
( NXT_PM.receiver_id = PM.sender_id AND NXT_PM.sender_id = PM.receiver_id )
)
)
AND U.username = 'Kacy'
ORDER BY time_sent DESC
This returns:
+----------+----------+----------+----------+---------------------+
| username | username | action | message | time_sent |
+----------+----------+----------+----------+---------------------+
| Kacy | liz | Sent | hi again | 2015-03-08 10:47:26 |
| Kacy | Jamie | Sent | hi | 2015-03-07 23:01:18 |
| Kacy | tracy | Received | hi | 2015-03-06 12:04:34 |
+----------+----------+----------+----------+---------------------+
3 rows in set (0.02 sec)
You can do as below technique for getting max per group
select
u1.username as sender,
u2.username as receiver,
m.message,
m.time_sent
from private_messages m
inner join users u1 on u1.id = m.sender_id
inner join users u2 on u2.id = m.receiver_id
left join private_messages m1 on m1.sender_id = m.sender_id
and m1.receiver_id = m.receiver_id
and m1.time_sent > m.time_sent
where m.sender_id = 1
and m1.id is null ;
You can also use
and m1.id > m.id
instead of
and m1.time_sent > m.time_sent
if id is auto-incremented in private_messages table
If you need to see data as both sender and receiver for a given user the condition would be as
where ( m.sender_id = 1 or m.receiver_id = 1 ) and m1.id is null
instead of
where m.sender_id = 1
and m1.id is null ;
http://dev.mysql.com/doc/refman/5.0/en/example-maximum-column-group-row.html

How to perform left outer join with distinct clause

I have the following data:
Users:
Id UserId Name
----------------
1 1 Him
2 10 Her
3 2 Other
Groups:
Id GroupId UserId
-------------------
1 1 1
2 2 2
3 3 10
In SQL I can do something like this to determine if a user is in any group
select *
from Users as u
left outer join (select distinct(UserId) from Groups) as g on u.UserId = g.UserId
The result should be something like this.
Id UserId Name UserId
------------------------
1 1 Him 1
2 10 Her 10
3 2 Other Null
But how can I do this in LINQ?
I think that this one is helpful for u,
var data = (from u in Users
join g in Groups.Where(a => a.UserId == (from gp in Groups.Select(r=>r.UserId).Distinct() ) )
on u.UserId equals g.UserId into outer
from x in outer.DefaultIfEmpty()
select u);
In function syntax, it would look like
var result = Users.Join(Groups,
U => U.UserId, G => G.GroupId,
(U,R) => R.Select(
G => new { Id = U.Id, UserId = U.UserId, Name = U.Name, UserId = G.UserId }
).DefaultIfEmpty(new { Id = U.Id, UserId = U.UserId, Name = U.Name, UserId = null })
);

Get conversation and last message Query

i'm tring to get the conversations with the relative last message and order them by time and if is read the message. Let's go to show the my logic.
I created 3 table: inbox_join / inbox_msg / users
On the first table "inbox join" i have the datas about who have a active discussion. In this case we have id_user - "1" and id_user_2 - "4" they have a conversation.
On the inbox_msg table I have the text message, id conversation where the message will shown and other field easy to understand.
Inbox join table
Inbox_msg table
Users table
I made a query that work fine, but the my issue is that i can't have the occured_at on the inbox_msg table. I would like find a better solution for have my desidered result and i can't order how i'm looking for.
This is my query
SELECT DISTINCT (
inbox_join.id_conversation
), user_chat.name AS name_conv, user_chat.surname AS surname_conv, user_chat.username as username_conv, user_chat.id as id_chat, image_upload.name_image, (
SELECT DISTINCT (
message
)
FROM inbox_msg
WHERE inbox_join.id_conversation = inbox_msg.id_conversation
ORDER BY occured_at DESC
LIMIT 1
) AS last_msg, users.name, users.surname
FROM inbox_join
INNER JOIN users ON users.id = inbox_join.id_user
INNER JOIN users AS user_chat ON user_chat.id <> 1 AND (inbox_join.id_user_2 = user_chat.id || inbox_join.id_user = user_chat.id)
INNER JOIN image_upload ON image_upload.id_image = user_chat.profile_image
WHERE inbox_join.id_user = 1
OR inbox_join.id_user_2 = 1
Result desidered selecting the conversation about user 1:
id_conversation | id_user | name | surname | username | last_msg | occured_at_last_msg | read_msg |
1 4 E S E Yes 1380724676 0
4 5 G E K Good 1380724675 0
Query:
SELECT im.id_conversation,
im.id_user,
u.name,
u.surname,
u.username,
im.message AS last_msg,
im.occured_at AS occured_at_last_msg,
im.read_msg
FROM inbox_msg im
JOIN users u
ON u.id_user = im.id_user
JOIN (SELECT id_conversation,
MAX(occured_at) AS occured_at
FROM inbox_msg
GROUP BY id_conversation) im2
ON im2.id_conversation = im.id_conversation
AND im2.occured_at = im.occured_at
ORDER BY im.occured_at DESC
I made this query and should work fine, I would like receive comment about this query.
SELECT DISTINCT (
im.id_conversation
), users.name, users.surname, users.username, image_upload.name_image, im.message as last_msg, im.occured_at, im.read_msg
FROM inbox_join
INNER JOIN (
SELECT sub . *
FROM (
SELECT DISTINCT (
id_conversation
), id_user, message, occured_at, read_msg
FROM inbox_msg
WHERE id_user <> 1
ORDER BY occured_at DESC
) AS sub
GROUP BY sub.id_user
ORDER BY sub.occured_at DESC
) AS im ON im.id_conversation = im.id_conversation
INNER JOIN users ON im.id_user = users.id
INNER JOIN image_upload ON users.profile_image = image_upload.id_image
WHERE inbox_join.id_user = 1 || inbox_join.id_user_2 = 1

How to find threads only posted by friends?

So, I have a majority of my SQL query done. However, I can't seem to figure out how select threads that were only posted by the users friends.
Here's my current query that works:
SELECT social_posts.message, social_posts.post_stamp, user.username, user.id, user.default_picture
FROM social_threads
JOIN social_posts ON social_posts.thread_id = social_threads.id
LEFT JOIN user ON user.id = social_threads.user_id
ORDER BY social_threads.id DESC
Here's the friends table:
*----*--------*--------*---------*
| id | user_a | user_b | request |
*----*--------*--------*---------*
| 1 | 3 | 5 | 1 |
*----*--------*--------*---------*
request set to one means the friend request was accepted.
The where clause in the query would have to look something like this:
WHERE ((friends.user_a = '3' || friends.user_b = '3') && friends.request = '1') || (social_threads.user_id = '3')
I just need to figure out how to get that where clause in the main query.
Any help is greatly appreciated.
You would have to Join the friends table, to keep only rows, in which the user is a friend of the thread poster. Something like this:
SELECT social_posts.message, social_posts.post_stamp, user.username, user.id, user.default_picture
FROM social_threads
JOIN social_posts ON social_posts.thread_id = social_threads.id
LEFT JOIN user ON user.id = social_threads.user_id
Join friends on (user.id = friends.user_a and social_threads.user_id = friends.user_b or user.id = friends.user_b and social_threads.user_id = friends.user_a) and friends.request = 1
ORDER BY social_threads.id DESC
use OR and AND operator
WHERE (friends.user_a = '3' OR friends.user_b = '3') AND friends.request = '1'

Returning first result where CASE result = X?

I'm trying to return to write a query that only returns first result where someid (result of the first case)=X - and ignoring subsequent results where someid=X. How would I alter the following query to achieve that?
Here's what I'm currently using:
SELECT DISTINCT CASE WHEN $userid != senderid THEN senderid ELSE GROUP_CONCAT(receivers.id SEPARATOR ', ') END someid,
CASE WHEN $userid != senderid THEN senders.username ELSE GROUP_CONCAT(receivers.username SEPARATOR ', ') END somename,
messages.body,
messages.time
FROM messages
LEFT JOIN messages_recipients AS recipients ON messages.id = recipients.messageid
LEFT JOIN users AS senders ON messages.senderid = senders.id
LEFT JOIN users AS receivers ON recipients.userid = receivers.id
WHERE recipients.userid = $userid
OR messages.senderid = $userid
GROUP BY messages.id
ORDER BY messages.time DESC
Based on the information from your comments, I have taken another stab at this.
I'm very hands-on so although I can conceptualize what will happen, I didn't know for sure till I started tinkering around.
So, let's start with the data. Based on what we talked about, I created three tables:
users
id user_name
1 Walker
2 John
3 Kate
messages
id senderid body time
1 1 ignored 1 2010-04-01 00:00:00.000
2 1 ignored 2 2010-04-02 00:00:00.000
3 3 ignored 3 2010-04-03 00:00:00.000
4 1 msg A to john and kate 2010-04-10 00:00:00.000
5 3 msg b from kate to walker and john 2010-04-11 00:00:00.000
*messages_recipients*
id messageid userid
1 1 2
2 1 3
3 2 2
4 3 1
5 4 2
6 4 3
7 5 1
8 5 2
The data is tailored in such a way that you (Walker) have both sent and received messages through the month of April with John and Kate.
You can see a list of these messages by running the following sql statement:
SELECT
u2.user_name AS Sender,
u1.user_name AS Receiver,
m.body,
m.time
FROM
messages m
JOIN
messages_recipients mr ON m.id = mr.messageid
JOIN
users u1 ON mr.userid = u1.id
JOIN
users u2 ON m.senderid = u2.id
ORDER BY
time DESC
Now, that we have the test scenario, the tricky part: returning the most recently communicate message between you (Walker) and John and Kate. I've got a rather long SQL statement and, admittedly, I am not the best at creating these, but I think this will still work:
BEGIN
DECLARE #UserId INT = 1
--A. Main Query
SELECT
CASE
WHEN mtemp.senderid = 1 --#UserId
THEN
CONCAT('Message To: ', receivers.user_name)
ELSE
CONCAT('Message From: ' , senders.user_name)
END AS MessageType,
mtemp.body,
mtemp.time
FROM
messages mtemp
INNER JOIN users senders ON
mtemp.senderid = senders.id
INNER JOIN
(
--B. Inner Query determining most recent message (based on time)
-- between #UserID and the person #UserID
-- Communicated with (either as sender or receiver)
select userid,max(maxtime) as maxmaxtime from
(
--C.1. First part of Union Query Aggregating sent/received messages on passed #UserId
SELECT
m2.body,
kk.*
FROM
`messages` m2 INNER JOIN
(
SELECT DISTINCT
userid,
MAX(m.time) AS MaxTime
FROM
messages m INNER JOIN
messages_recipients mr ON m.id = mr.messageid AND
m.senderid = 1 --#UserId
GROUP BY
mr.userid
) kk on m2.time = kk.MaxTime and m2.senderid = 1 --#UserId
UNION
--C.2. Second part of Union Query Aggregating sent/received messages on passed #UserId
SELECT
m1.body,
jj.*
FROM
`messages` m1 INNER JOIN
----C.2a. Inner most query of users users who sent message to userid
(SELECT DISTINCT
senderid as userid,
MAX(m.time) AS MaxTime
FROM
messages m INNER JOIN
messages_recipients mr ON m.id = mr.messageid AND
mr.userid = 1 --#UserId
GROUP BY
m.senderid) jj on m1.time = jj.MaxTime and m1.senderid = jj.userid
) MaximumUserTime
group by
MaximumUserTime.userid
) AggregatedData on mtemp.time = AggregatedData.maxmaxtime
INNER JOIN users receivers on AggregatedData.userid = receivers.id
ORDER BY `time` DESC
END
To test in phpMyAdmin, you'll have to remove the comments and the begin/end declare statements as well. I just wanted to post this as if it would look in a procedure.
The query assumes that you won't both send and receive a different message from the same user at the exact same time; the odds of that happening seem very small so I'm hoping may this will work.
When I run this query I get the following results:
MessageType body time
Message From: Kate msg b from kate to walker and john 2010-04-11 00:00:00.000
Message To: John msg A to john and kate 2010-04-10 00:00:00.000
That's the most recent communications concerning Walker among all those users who have communicated with Walker.
Hope that helps.
A sub-query may work in this case (though the performance of it may not make it a great option):
SELECT DISTINCT CASE WHEN $userid != senderid THEN senderid ELSE GROUP_CONCAT(receivers.id SEPARATOR ', ') END someid,
CASE WHEN $userid != senderid THEN senders.username ELSE GROUP_CONCAT(receivers.username SEPARATOR ', ') END somename,
messages.body,
messages.time
FROM messages
INNER JOIN
(
SELECT m.senderId, m.receiverId, MAX(m.time) AS MyMaxTime FROM messages GROUP BY m.senderId, m.receiverId
) myLittleQuery
ON messages.senderid = myLittleQuery.senderid AND messages.senderid = myLittleQuery.receiverId AND messages.time = myLittleQuery.MyMaxTime
LEFT JOIN messages_recipients AS recipients ON messages.id = recipients.messageid
LEFT JOIN users AS senders ON messages.senderid = senders.id
LEFT JOIN users AS receivers ON recipients.userid = receivers.id
WHERE recipients.userid = $userid
OR messages.senderid = $userid
GROUP BY messages.id
ORDER BY messages.time DESC
Been a while since I've been in the MySQL realm so my syntax is most likely off. However, the gist of it is there.
Also, depending on what you're trying to retrieve, you might be able to ditch the DISTINCT and CASE statements as well.