简体   繁体   中英

Weird Result From a MySql Query

I have three tables:

  1. users (all the users)
  2. friends (relation table consists of ID1 and ID2 columns which basically tells who's friend with who)
  3. posts (all the posts with the ID of the user who has written them).

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM