想象一下这 3 个表:
User Login: LoginInfo:
---- ----- -----
id:integer id:integer id:integer
name:string user_id:integer login_id:integer
是否可以在不执行子查询/子选择的情况下选择所有没有 LoginInfos
的用户(我写这个时没有对照数据库检查):
select id from users
where id not in (
select distinct(user_id) from logins
right join login_infos on logins.id = login_infos.login_id
)
我正在使用 Postgres 作为数据库。
最佳答案
为什么要使用 RIGHT JOIN
?
您可能会将 NULL
添加到子查询的结果中,这是 NOT IN
构造中的常见陷阱。
考虑这个简单的演示:
SELECT 5 NOT IN (VALUES (1), (2), (3)) --> TRUE
,5 NOT IN (VALUES (1), (2), (3), (NULL)) --> NULL (!)
,5 IN (VALUES (1), (2), (3)) --> FALSE
,5 IN (VALUES (1), (2), (3), (NULL)) --> NULL (!)
换句话说:
“我们不知道 5
是否在集合中,因为至少有一个元素是未知的,可能是 5。”
因为在 WHERE
子句中只有 TRUE
是相关的(NULL
和 FALSE
都没有通过测试),它不会根本不会影响 WHERE id IN (...)
。
但是影响
WHERE id <b>NOT IN</b> (...)
只要右侧集合中存在 NULL
,此表达式 never 就符合条件。可能不是预期的那样?
解决方案
要求是
select all the Users that don't have LoginInfos
这可以包括在表 login
中也没有行的用户。因此,我们需要 LEFT JOIN
两次。由于未定义一个用户是否可以在 login
中有多行,因此我们还需要 DISTINCT
或 GROUP BY
:
SELECT DISTINCT u.*
FROM users u
LEFT JOIN login l ON l.user_id = u.id
LEFT JOIN logininfo i ON i.login_id = l.id
WHERE i.login_id IS NULL
这涵盖了所有可能发生的情况。你可以 ...
- 如果每位用户至多一次登录,则移除
DISTINCT
。 - 如果每个用户在
login
中至少一行,则将第一个LEFT JOIN
替换为JOIN
.
这个替代 NOT EXISTS
使用子查询。
但无论每个用户在表 login
中有多少行,它都有效。而且它没有出现上述任何 NOT IN
问题:
SELECT u.*
FROM users u
WHERE NOT EXISTS (
SELECT 1
FROM login l
JOIN logininfo i ON i.login_id = l.id
WHERE l.user_id = u.id
)
关于sql - 可以在没有子查询的情况下进行此查询吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15481203/