I have three tables:
I want to select all the posts that the user wrote from the posts table and the posts written by his friends specified by the friends table, I wrote this query:
SELECT
posts.ID as ID,
CONCAT(users.FirstName, ' ', users.LastName) as Name,
posts.Date as Date,
posts.Text as Text
FROM users, posts, friends
WHERE users.ID = posts.UserID AND
((posts.UserID = friends.ID1 AND friends.ID2 = '10000007') OR
(posts.UserID = friends.ID2 AND friends.ID1 = '10000007')
OR (posts.UserID = '10000007'))
this gives me the posts written by his friends with many extra rows that I don't know where it came from. So can anybody tell me where is the problem here in the query?
Personally I think using that type of select syntax (SELECT ... FROM table1, table2, table3) is a little confusing to read and I wouldn't doubt it if you see some weird behavior because of it. How about this instead:
SELECT DISTINCT
posts.ID as ID,
CONCAT (users.FirstName, ' ', users.LastName) as Name,
posts.Date as Date,
posts.Text as Text
FROM
users
LEFT JOIN posts ON users.ID = posts.UserID
LEFT JOIN friends ON (
(posts.UserID = friends.ID1 AND friends.ID2 = '10000007') OR
(posts.UserID = friends.ID2 AND friends.ID1 = '10000007'))
HAVING
posts.UserID IS NOT NULL OR friends.ID1 IS NOT NULL
I would write it this way.
SELECT posts.ID as ID,
CONCAT(users.FirstName, ' ', users.LastName) as Name,
posts.Date as Date,
posts.Text as Text
FROM users
LEFT JOIN posts
ON users.ID = posts.UserID
OR posts.UserID IN
(SELECT ID2 FROM friends WHERE ID1 = '10000007'
UNION
SELECT ID1 FROM friends WHERE ID2 = '10000007')
WHERE users.ID = '10000007'
If you use IN
, there's no need of nesting join friends table and users table:
SELECT
posts.ID as ID,
users.ID as UserID,
CONCAT(users.FirstName, ' ', users.LastName) as Name,
posts.Date as Date,
posts.Text as Text
FROM users, posts -- Try to change the old style join to
-- FROM users JOIN posts
-- ON users.ID = posts.UserID Where ...
WHERE users.ID = posts.UserID
AND (posts.UserID = '10000007' OR
posts.UserID IN
(
SELECT ID
FROM friends
WHERE friends.ID1 = '10000007'
UNION
SELECT ID
FROM friends
WHERE friends.ID2 = '10000007'
)
)
Your original query will return duplicate rows because you join to friends table based on one to many relationship. You have two choice to change it. One way is add distinct to your select statement. The other way is using exists like this(If there are too many fiends for a user, EXISTS
will be more efficient than IN
):
SELECT
posts.ID as ID,
CONCAT(users.FirstName, ' ', users.LastName) as Name,
posts.Date as Date,
posts.Text as Text
FROM users
JOIN posts
ON users.ID = posts.UserID
WHERE
EXISTS
(SELECT 1
FROM friends
(posts.UserID = friends.ID1 AND friends.ID2 = '10000007')
OR
(posts.UserID = friends.ID2 AND friends.ID1 = '10000007')
)
OR posts.UserID = '10000007'
I was able to solve my problem using nested SELECT statement:
SELECT
posts.ID as ID,
users.ID as UserID,
CONCAT(users.FirstName, ' ', users.LastName) as Name,
posts.Date as Date,
posts.Text as Text
FROM users, posts
WHERE users.ID = posts.UserID AND
(posts.UserID = '10000007' OR
posts.UserID IN (
SELECT users.ID
FROM users, friends
WHERE (friends.ID1 = '10000007' AND users.ID = friends.ID2)
OR (friends.ID2 = '10000007' AND users.ID = friends.ID1)
))
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.