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

时间:2015-10-16 17:45:38

标签: sql sqlite relational-division

我想找到接受所有贷款类型的借款人。

架构:

loan (number (PKEY), type, min_rating)
borrower (cust (PKEY), no (PKEY))

示例表:

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

这里的答案是“汤姆”。

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

我想使用双重否定,我首先找到没有获得所有贷款的借款人,并找到不在那个套房中的借款人。我想使用NOT EXISTS嵌套,我首先找到没有获得所有贷款的借款人,但我无法为此创建一个有效的查询。

3 个答案:

答案 0 :(得分: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, '')) > ''

分组式整齐地回避了不止一次选择人的问题(以及经常需要的丑陋子查询),并依赖于一个非常合理的假设,即贷款号永远不会是空白。即使这是可能的,你仍然可以找到一种方法来使这个模式工作(例如将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
)

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

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

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

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
);

这意味着您不需要distinct运算符(留给您找出原因),但也可以处理现实生活情况,例如两个具有相同名称的借款人。

答案 1 :(得分:1)

我认为良好的第一步是采用借款人和贷款的笛卡尔积*,然后使用where子句过滤到那些不会出现在你的借款人和##中的那些" 34;表。 (虽然我认为那会使用NOT IN而不是NOT EXISTS,所以可能不是你的想法?)

(*需要注意的是笛卡尔产品是一件非常糟糕的事情,在现实生活中你需要仔细考虑性能)

ETA:NOT EXISTS变体可能如下所示: 像以前一样获取笛卡尔积,为借方和贷款的组合做一个相关的子查询,然后使用带有NOT EXISTS条件的WHERE子句过滤该查询是否返回任何行。

答案 2 :(得分:0)

select cust from borrower
except
select t.cust
from (select distinct cust,number
      from borrower cross join loan) t
left join borrower b on t.cust = b.cust and t.number = b.num
where b.num is null

Fiddle

从借款人和贷款中借助cross join客户。然后left join borrower表找到没有获得所有贷款的客户。最后,使用except选择已获得所有贷款的客户。