Reputation: 1192
I have 3 databases setup for a Twitter-clone website.
Table 'users':
email | firstname | lastname | hash_password
Link Table 'friends':
email | friend
This lists all users and their friends. Friendships are 1-way (like Twitter's "Following"/"Followers").
friends
table is a link table to list all users' friends. So both friends.user
and friends.friend
are foreign keys to users.email
.
Table 'messages':
timestamp | user | content
What is an SQL query to retrieve all messages from a user and his friends?
I've tried:
SELECT
'timestamp',
user,
content
FROM
messages
INNER JOIN friends ON messages.user = friends.friend
ORDER BY 'timestamp' DESC;
This seems to join them correctly, but how can I only get the messages for a specific user's (email address) friends. Right now this just returns all messages.
Thanks.
Upvotes: 1
Views: 1754
Reputation: 270775
The quoting of timestamp
should be with backticks, otherwise MySQL will assume a string literal value.
Otherwise, to return messages from both the user and all friends, you may use a UNION
. One half returns the user's messages, the other half returns friends' messages. You need to add the user's email to your join condition:
/* First part of the UNION returns friends messages */
SELECT
`timestamp`,
user,
content
FROM
messages
/* The join ON clause specifies the main user email */
INNER JOIN friends
ON messages.user = friends.friend
AND friends.email = 'email@example.com'
UNION ALL
/* Other part of the UNION just gets all messages for the main user by email addr */
SELECT
`timestamp`,
user,
content
FROM
messages
WHERE user = 'email@example.com'
/* ORDER BY applies to everything */
ORDER BY `timestamp` DESC;
And if you want to join in the users
info (firstname/lastname
) here, the easiest way to go about it would be to wrap the entire thing in a subquery and join.
SELECT
users.*,
messages_sub.content,
messages_sub.`timestamp`
FROM
users
JOIN (/* The entire thing from above */) AS messages_sub ON users.email = messages_sub.user
ORDER BY `timestamp`
It can also be done with a UNION
of the literal email address you want to find and the friend list, producing only one outer query. This is a little trickier, but may be ultimately faster. It will also be less confusing to bring in other columns from the users
table here. I'll add the names:
SELECT
`timestamp`,
user,
firstname,
lastname,
content
FROM
messages
INNER JOIN (
/* Union query produces a list of email addrs -
the person you're looking for plus all his friends
Starts with string literal for the main user
*/
SELECT 'email@example.com' AS email
UNION
/* Plus all his friends into one list joined against messages */
SELECT friend AS email FROM friends WHERE email = 'email@example.com'
) user_and_friends ON messages.user = user_and_friends.email
/* Join against the users table for name info */
INNER JOIN users ON user_and_friends.email = users.email
ORDER BY `timestamp` DESC
Upvotes: 2