使用NOT EXISTS获得所有贷款的借款人

mar*_*gor 6 sql sqlite relational-division

我想找到所有贷款类型的借款人.

架构:

loan (number (PKEY), type, min_rating)
borrower (cust (PKEY), no (PKEY))
Run Code Online (Sandbox Code Playgroud)

样本表:

number | type     | min_rating
------------------------------
L1     | student  | 500
L2     | car      | 550
L3     | house    | 500
L4     | car      | 700
L5     | car      | 900

cust  | no 
-----------
Jim   | L2
Tom   | L1
Tom   | L2
Tom   | L3
Tom   | L4
Tom   | L5
Bob   | L3
Run Code Online (Sandbox Code Playgroud)

这里的答案是"汤姆".

我可以简单地计算贷款总数,并将借款人的贷款数量与之比较,但我不允许(这是一项家庭作业),为了这个家庭作业和学习的目的.

我想使用双重否定,我首先找到没有获得所有贷款的借款人,并找到那些不在那里的借款人.我想NOT EXISTS在我第一次找到没有拿到所有贷款的借款人的地方使用嵌套,但是我无法为此创建一个有效的查询.

Boh*_*ian 3

一个简单的方法是使用事实:

  • 当没有连接时,外部连接会给你空值
  • coalesce() 可以将 null 转换为空白(始终小于实际值)

因此,没有每种贷款类型的人的最小合并贷款数量将为空白:

select cust
from borrower b
left join loan l on l.number = b.no
group by cust
having min(coalesce(l.number, '')) > ''
Run Code Online (Sandbox Code Playgroud)

分组巧妙地回避了多次选择人员的问题(以及经常需要的丑陋的子查询),并依赖于贷款号码永远不会为空的相当合理的假设。即使这是可能的,您仍然可以找到一种方法来使该模式发挥作用(例如将 min_ rating 合并为负数等)。

可以使用表达式重写上面的查询,可能更具可读性NOT IN

select distinct cust
from borrower
where cust not in (
  select cust
  from borrower b
  left join loan l on l.number = b.no
  where l.number is null
)
Run Code Online (Sandbox Code Playgroud)

通过利用错过的联接返回所有空值的事实,内部查询的 where 子句仅保留错过的联接。

您需要使用DISTINCT来阻止借款人出现两次。


您的架构有问题 - 借款人和负载之间存在多对多关系,但您的架构对此处理不佳。borrower每个人应该有一行,还有另一个关联表来记录借款人贷款的事实:

create table borrower (
    id int,
    name varchar(20)
    -- other columns about the person
);

create table borrrower_loan (
    borrower_id int, -- FK to borrower
    load_number char(2) -- FK to loan
);
Run Code Online (Sandbox Code Playgroud)

这意味着您不需要distinct操作员(留给您找出原因),但也可以处理现实生活中的情况,例如两个同名的借款人。