0

我目前正在为 MySQL 加入而苦苦挣扎。

我有 3 个表,用户、礼物和 users_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

4 回答 4

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
  )
于 2013-10-14T20:52:18.450 回答
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)
于 2013-10-14T20:51:40.707 回答
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
   )

哪种方法更快,或者一种方法是否比另一种更快,取决于您的具体情况。根据这篇文章,该NOT EXISTS方法慢了 30%,但这篇文章建议它NOT EXISTSLEFT JOIN列包含 NULL 时性能更高。正如大流士在对他的回答的评论中提到的那样,“测试是要走的路。”

于 2013-10-14T20:54:29.243 回答
0
SELECT *
FROM   gifts
WHERE  gifts.id NOT IN (SELECT gift_id
                        FROM   user_gifts
                        WHERE  uid = 3);
于 2013-10-14T21:09:22.220 回答