Mysql facebook style message join 3 tables - php

//USER TABLE
user_id name
1 ben
2 alex
3 john
//CONVERSION TABLE
c_id user_one user_2
1 2(alex) 1(ben)
2 2(alex) 3(john)
3 1(ben) 3(john)
//MESSAGE TABLE
m_id c_id send receive message
1 1 2(alex) 1(ben) hi ben
2 1 2(alex) 1(ben) ben, u there?
3 2 1(ben) 3(john) whatever...
//QUERY 1
SELECT * FROM conversion WHERE user_one=1(ben)
OR user_two=1(ben)
So now i know ben have 2 conversations (one with alex another with john)
my question is
how to join 3 tables and fetch out like this
conversation_1 - Alex(id=2) - Last message in cv_1(ben, u there?)
conversationi_3 - John(id=3) - Last message in cv_3(whatever...)
like facebook message

The main idea is that you have to use joins. Standard JOIN syntax will be of no help here because you cannot have OR statement in JOIN .. ON. But something like this will do the trick
SELECT c.c_id, u.user_id, u.name, MAX(m_id), message FROM message m, conversation c, user u
WHERE m.c_id = c.c_id
AND
(
c.user_one = u.user_id
OR
c.user_2 = u.user_id
)
GROUP BY c.c_id
Here we join 3 tables together, getting maximum message ID (I assume ID is auto incremental so it is safe to assume that the higher ID the older the message) and group by conversation id. This is how we will have the oldest message and conversation details of messages where Ben (logged in user for instance) was involved
A nice article I saw somewhere on stack overflow before is http://www.khankennels.com/blog/index.php/archives/2007/04/20/getting-joins/. The current approach is INNER JOIN.
SQL Fiddle - http://sqlfiddle.com/#!2/ae6e6/14

Related

Sort the results of the first table by the id of the second

I'm sorting some users by id (oldest to newest).
This is my accounts table:
Accounts
id
name
1
James
2
Kirk
3
Roberto
4
Lars
However, I need to improve this ordering by relating a second messages table.
Accounts_Messages
id
sender_id
receiver_id
1
1
4
2
1
2
3
1
3
In this case, users in Accounts table should be ordered by last messages.
This is the expected result:
Roberto;
Kirk;
Lars;
The question is: How can I sort the results of the first table by the id of the second?
I read that I need to use `JOIN` to relate these two tables, but I didn't know how to apply it in this specific case.
Thank you, guys!
Hey use this MySQL query for your aspected result
Select Accounts.name from Accounts_Messages left join Accounts on Accounts.id = Accounts_Messages.receiver_id order by Accounts_Messages.id DESC
this is what I understand by your question you need to sort the accounts by the id of the accounts_Message
I think you should try
SELECT acc.* FROM Accounts_Message am LEFT OUTER JOIN Accounts acc USING(id) ORDER BY id ASC
if both Accounts and Accounts_Message has same column name id use USING(id) or the column name is different use ON acc.id = am.id instead

PHP Mysql Relations

id message round date user_id video_id
1 TextMsg 1 2018-01-26 1 1
2 TextMsg 1 2018-01-26 2 1
3 TextMsg 2 2018-01-26 2 6
I have this table 'messages 'on my Mysql database,
with relations user_id to my users table and video_id to my videos table
using php how can I make a loop that shows
the messages like a chat.
All the messages related to the video
SELECT * FROM messages WHERE $id = videos.id // I doubt
then
SELECT * FROM messages WHERE round = 1 // need to make the rounds as tabs
then
SELECT * FROM users WHERE user_id = id
I'm completely lost, any help would be awesome,
Appreciate.
You just need to do a left join.
SELECT * FROM messages
LEFT JOIN videos ON videos.id=messages.video_id
LEFT JOIN users ON users.id=messages.user_id
WHERE video_id={$id} GROUP BY rounds ORDER BY date DESC
Take note of the duplicate columns, rename if necessary.

PHP multiQuery issue

I'm having a bit of an issue regarding multi-querying with PHP. My database has the following tables, with their schema shown below:
First table - Users
(PK) UserID,
UserFullName
UserID FullName
1000 Arthur Whitney
2000 Alex Schwartz
3000 Eva Kilpatrick
Second table - Invites
(PK) RowID,
InvName, InvSender, InvSenderTotal, InvReceiver, InvReceiverTotal, InvStatus
RowID InvName InvSenderID InvSenderTotal InvReceiverID InvReceiverTotal InvStatus
1 Fair 1000 10 2000 10 Sent
2 Party 2000 45 1000 45 Sent
3 Cinema 2000 12 1000 12 Sent
4 Vacation 3000 15 1000 5 Expired
Say for example when user Alex Schwartz, with unique ID 2000, is logged in, I would like to have it that he can see the name of the invite, the invite sender's name (if he is the invite receiver), the invite receiver's name (if he is the invite sender) and both the invite sender and receiver's totals.
I have devised the following two queries, which yield the results that I intended:
QUERY1
select i.InvName, i.InvSenderTotal, i.InvReceiverTotal, u.FullName FROM Invites as i INNER JOIN Users as u ON u.UserID = i.InvReceiverID
WHERE i.InvStatus = 'Sent' AND i.InvSenderID = 2000
This shows me the Invite Name, Invite Sender and Receivers' totals and the Invite Receiver's name when Alex Schwartz was sending the invite. Expected output here would be specific details of Row IDs 2 and 3.
The following query shows the inverse:
QUERY2
select i.InvName, i.InvSenderTotal, i.InvReceiverTotal, u.FullName FROM Invites as i INNER JOIN Users as u ON u.UserID = i.InvSenderID
WHERE i.InvStatus = 'Sent' AND i.InvReceiverID = 2000
So this enables Alex Schwartz to see the name of the person who sent the invite, along with the same data. Expected output here would be specific details of Row ID 1.
Hence, I am able to get the data I want, but I require two different queries in order to get it. This has presented problems in PHP whereby only one query seems to carry out. So my question is essentially, does anyone know how I could combine these two queries into one? I am also aware of multiQuerying and have tried to implement this, but have not had much success with that either.
Thanks in advance of your help.
Why not use UNION.
select i.InvName, i.InvSenderTotal, i.InvReceiverTotal, u.FullName FROM Invites as i INNER JOIN Users as u ON u.UserID = i.InvReceiverID
WHERE i.InvStatus = 'Sent' AND i.InvSenderID = 2000
UNION
select i.InvName, i.InvSenderTotal, i.InvReceiverTotal, u.FullName FROM Invites as i INNER JOIN Users as u ON u.UserID = i.InvSenderID
WHERE i.InvStatus = 'Sent' AND i.InvReceiverID = 2000
As a note, you can do this without union all (which is preferred over union unless you intentionally need to remove duplicates):
select i.InvName, i.InvSenderTotal, i.InvReceiverTotal, u.FullName
FROM Invites i INNER JOIN
Users u
ON u.UserID IN (i.InvReceiverID, i.InvSenderID)
WHERE i.InvStatus = 'Sent' AND i.InvSenderID = 2000;

Matching interests(Nearest neighbour) search in SQL

I'm trying to find users with similar set of interests, with the following schema..
USERS - ID name etc
Interests - ID UID PID
where ID is unique ID for Interests, UIS is user ID and PID is a product ID. I have looked at other similar questions at SO, but none of them had an exact answer.
Example- Let's say I'm interested in getting users with similar interest to John, and this is how to two tables look like ...
ID Name
11 John
12 Mary
13 Scott
14 Tim
ID UID PID
3 12 123
4 12 231
5 12 612
6 13 123
7 13 612
8 14 931
9 14 214
10 11 123
11 11 231
12 11 781
13 11 612
I would like a result with in that order.
I was thinking of doing a set intersection of the user I'm interested in with all other users. It doesn't sound like a very good solution, because it will have to be done everytime a user adds interest or another user is added. Its a small project, and as of now I'll be limiting users to 100. I still think that the above approach will not be efficient at all as it will take 1002 time.
Can someone guide me in the right direction? What are the possible solutions, and which one will be the best with above given constraints. I'm looking at ANN to see if I can use that.
This starts by counting the number of interests that each user has in common with John. The approach is to take all of John's interests, join back to the interests table and aggregate to the the count of common interests. Here is the SQL for that:
select i.uid, COUNT(*) as cnt
from (select i.*
from interests i join
users u
on i.uid = i.id
where u.name = 'John'
) ilist join
interests i
on ilist.pid = i.pid and
ilist.uid <> i.uid -- forget about John
group by i.uid
But, you actually want the list of products, rather than just the count. So, you have to join back to the interests table:
select i.*
from (select i.uid, COUNT(*) as cnt
from (select i.*
from interests i join
users u
on i.uid = i.id
where u.name = 'John'
) ilist join
interests i
on ilist.pid = i.pid and
ilist.uid <> i.uid -- forget about John
group by i.uid
) t join
interests i
on t.uid = i.uid
group by t.cnt, i.uid
The following query finds others users with atleast 2 or more similar interests according to the interests of user 11.
SELECT in2.UID FROM users u
INNER JOIN interest in1 ON (in1.UID = u.ID)
INNER JOIN interest in2 ON (in2.PID = in1.PID AND in2.UID <> u.ID)
WHERE u.ID = 11
GROUP BY in2.UID
HAVING COUNT(in2.UID) >= 2
ORDER BY COUNT(in2.UID) DESC
The ORDER BY ensures that users with the most similar interests ends up first. The HAVING COUNT(in2.UID) >= 2) makes sure the users which are found have atleast 2 or more similar interest.

Getting the latest message between all conversations with other users

I'm trying to pull out of the db a list of each last message the current user had sent to/received from another, like the facebook current messaging box (contains a list of "conversations")
Example data:
msg_id sender_id target_id date_sent content ...
1 2 4 20 bla ...
2 2 5 21 bla ...
3 2 6 22 bla ...
4 4 2 25 bla ...
5 5 6 26 bla ...
6 4 2 50 bla ...
If the current user is 2, then I want to get only the last message 2 had with anyone else (sent or received)
Wished data would be:
msg_id sender_id target_id date_sent content ...
6 4 2 50 bla ...
2 2 5 21 bla ...
3 2 6 22 bla ...
msg_id 6 is there because in all the messages 2 and 4 had (regardless of who is the sender/receiver) it has the greatest date (50)
msg_id 2 and 3 are there because that's the latest msg 2 had in conversation with users 5 and 6 (one msg to each, sent)
Couldn't find the way to pull this off, should involve a group_by on some uniquely generated field containing both sender and receiver IDs? I don't know, help please
UPDATE:
I liked the ideas, eventually I created a view of that table with another new field, that contains a concatenation of the two Id's, in order (bigger first), seperated by an underscore. This way that field is unique to each conversation. Then group by it :)
Here's an untested example. There are other ways to do it, this is a pretty common question if you do a search.
SELECT t1.*
FROM
msg_table AS t1
LEFT JOIN msg_table AS t2
ON ((t1.sender_id = t2.sender_id AND t1.target_id = t2.target_id)
OR (t1.sender_id = t2.target_id AND t1.target_id = t2.sender_id))
AND t1.msg_id < t2.msg_id
WHERE (t1.sender_id = ? OR t1.target_id = ?) AND t2.msg_id IS NULL
You could probably do something where you combine sender_id and target_id into a computed value and then do a "LIKE" query on that.
But, to be honest, I suspect your performance will be better just running two simpler queries and then merging the results in your code. Your code will be easier to maintain, as well.
That could be a bit messy.
Had a quick play and something like this will do the job. However this will get the latest message sent to a sender AND the latest sent by a sender (rather than the latest one of those which it seems you want), along with the latest message to each recipient from that sender.
Not tested so please excuse any typos.
SELECT a.msg_id, a.sender_id, a.target_id, a.date_sent, a.content
FROM someTable a
INNER JOIN (SELECT sender_id As SomeBod, MAX(date_sent) AS date_sent
FROM someTable
WHERE sender_id = 2
GROUP BY sender_id) sub1
ON a.sender_id = sub1.SomeBod
AND a.date_sent = sub1.date_sent
UNION
SELECT b.msg_id, b.sender_id, b.target_id, b.date_sent, b.content
FROM someTable b
INNER JOIN (SELECT target_id As SomeBod, MAX(date_sent) AS date_sent
FROM someTable
WHERE target_id = 2
GROUP BY target_id) sub2
ON b.target_id = sub2.SomeBod
AND b.date_sent = sub2.date_sent
UNION
SELECT c.msg_id, c.sender_id, c.target_id, c.date_sent, c.content
FROM someTable c
INNER JOIN (SELECT sender_id, target_id, MAX(date_sent) AS date_sent
WHERE sender_id = 2
GROUP BY sender_id, target_id) sub3
ON a.sender_id = sub3.sender_id
AND b.target_id = sub3.target_id
AND b.date_sent = sub3.date_sent
I did something similar to what you're shooting for here. But to make it easier, i added another column to the table called convoID. This is not a unique id, but an id that is reused every time the user sends or receives a message from a user they have allready communicated with. then get all the convoIDs from the table(but not duplicates)
SELECT DISTINCT convoID FROM table WHERE sender='$user' OR recipient='$user'
This way every message between the same 2 people is easy to access as a conversation.From there you can get the newest message from a conversation like this:
SELECT message FROM table WHERE convoID='$thisConvoID' ORDER BY time DESC LIMIT 0,1;
For me this was the least confusing way to do it, and worked great- if you need clarification on anything just ask.

Categories