全选并排除结果。 SQL连接

时间:2013-10-14 20:47:42

标签: mysql sql database

目前我正在努力加入MySQL。

我有3个表,用户,礼物和用户_gifts。

- Users
    - id
    - username
    - email
    - password

- Gifts
    - id
    - gift
    - value

- users_gifts
    - uid
    - gift_id

我想要退回所有礼物,但不包括用户已经发送的礼物。所以,如果可用的礼物是:

card
heart
necklace
perfume

如果用户已经发送了一个心脏,那么唯一会返回的礼物是

card
necklace
perfume

我尝试了以下连接,但由于某种原因,它没有达到预期的效果。

SELECT *
FROM   gifts
JOIN   users_gifts
ON     gifts.id = user_gifts.gift_id
WHERE  users_gifts.uid != 3

...假设当前用户的用户ID是3。

我在这里遗漏了什么,或者我的SQL有错误吗?

4 个答案:

答案 0 :(得分:4)

您的查询正在寻找#3以外的用户。

相反,您需要查看users_gifts,对于user = 3。

但是,您想要排除该子查询中存在的任何礼物:

SELECT * 
FROM   gifts 
WHERE NOT EXISTS
  (SELECT 1 
   FROM   users_gifts 
   WHERE  users_gifts.uid = 3
   AND    user_gifts.gift_id = gifts.id
  )

答案 1 :(得分:2)

你可以使用NOT EXISTS子句,它不像join那样高效,但对于这种请求更具可读性(在我看来)。

select * from gifts g
where not exists (select null 
                  from user_gifts ug
                  where g.id= ug.gift_id
                  and ug.uid = 3)

答案 2 :(得分:2)

我建议你改变方法。

目前,您正在尝试选择#3以外的用户发送的所有礼物。此外,您正在使用内部联接,这意味着包含除#3以外的用户礼物发送。因此,任何人从未发送的礼物都被排除在外。

相反,您应该从所有礼物开始,然后对用户#3执行外部加入。如果用户已发送礼物,则会返回所有礼品列和用户列。如果用户没有发送礼物,那么用户列将只包含NULL,但仍将包含该行。

以下是外部联接的示例,该外部联接将检查用户列以确定用户是否已发送礼物。只有包含NULL的列才包含在最终结果中,这意味着用户发送了礼物:

SELECT *
FROM gifts
LEFT JOIN users_gifts
  ON users_gifts.gift_id = gifts.id
  AND user_gifts.uid = 3 -- Whatever the User's ID is
WHERE users_gifts.gift_ID IS NULL -- exclude the gift if it's already been sent

这是使用EXISTS子查询的替代方法:

SELECT *
FROM gifts
WHERE NOT Exists(
     SELECT 0
     FROM users_gifts
     WHERE users_gifts.gift_id = gifts.id
     AND user_gifts.uid = 3 -- Whatever the User's ID is
   )

哪种方法更快,或者一种方法是否比另一种更快,取决于您的具体情况。根据{{​​3}},NOT EXISTS方法慢30%,但this article表示如果列包含NULL,NOT EXISTS的性能高于LEFT JOIN。{{1}}正如他对答案的评论中的this article一样,“测试是要走的路。”

答案 3 :(得分:0)

SELECT *
FROM   gifts
WHERE  gifts.id NOT IN (SELECT gift_id
                        FROM   user_gifts
                        WHERE  uid = 3);