可以在没有子查询的情况下进行此查询吗?

时间:2013-03-18 15:55:48

标签: sql postgresql null left-join sql-in

想象一下这三个表:

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作为数据库。

2 个答案:

答案 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是相关的(NULLFALSE都没有通过测试),因此根本不会影响WHERE id IN (...)

但它会影响

WHERE id NOT IN (...)
只要右侧设置NULL,此表达式永远不会符合条件。
可能不是预期的?


解决方案

要求是

  

选择所有没有LoginInfos的用户

这可以包括表login中没有行的用户。因此,我们需要LEFT JOIN两次。由于未定义一个用户是否可以在login中拥有多行,因此我们还需要DISTINCTGROUP 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