目前我正在努力加入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有错误吗?
答案 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);