想象一下这三个表:
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作为数据库。
答案 0 :(得分:2)
为什么 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 NOT IN (...)
只要右侧设置NULL
,此表达式永远不会符合条件。
要求是
选择所有没有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
。LEFT JOIN
中至少一行,则将JOIN
替换为login
。这个带有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
)
答案 1 :(得分:0)
select distinct u.id
from
user u
inner join
login l on u.id = l.user_id
left join
logininfo li on l.id = li.login_d
where li.id is null