【问题标题】:How to count re-borrowers that had a lower interest rate than the existing old ones?如何计算利率低于现有旧借款人的再借款人?
【发布时间】:2018-01-29 16:15:20
【问题描述】:

我有一张贷款记录表,其中包含年利率 (annual_interest_rate)、单调递增顺序 ID (loan_id) 和借款人 ID (member_id)。

我正在尝试计算新贷款利率低于之前贷款的再借款人(即拥有多笔贷款的会员)。

下面给出了一个数据样本:

Annual_interest_rate | Loan_id | Member_id
 0.2850              |  1      | -9832
 0.1482              |  2      |  6982
 0.065               |  3      | -9832
 0.1754              |  4      |  1234
 0.2387              |  5      |  1234

在这个样本数据中,唯一一个其后期贷款利率低于之前贷款利率的再借款人是成员 -9832,因此此类再借款人的总数应该是一个。

如何计算此类再借款人的总数?

【问题讨论】:

  • Martijn Pieters 我想附上一张我桌子的照片,但它不允许我,除非我有 10 分......有没有办法做到这一点?
  • Ilmari Karonen 我重新编辑了表格。因此,id 为 -9832 的个人在 member_id 列中出现了两次,但在 Annual_interest_rate 中,他获得的利率与 -9832 不同。这是更好的解释吗?希望!我是 SQL 新手,甚至是一般的编码新手,所以我什至无法用语言表达什么是正确的 :) 抱歉
  • 我正在寻找以较低利率重新借款的总人数。再次抱歉没有说清楚。

标签: python sql sqlite


【解决方案1】:

有几种不同的方法可以使用 SQL 来解决这个问题。所有这些都是非常标准的 SQL 查询,并且应该适用于任何或多或少 ANSI SQL 兼容的数据库,而不仅仅是 SQLite。

也许概念上最简单的方法是使用subquery 来查找存在另一个具有相同成员 ID 和较低利率的后来的贷款记录的贷款记录:

SELECT DISTINCT Member_id
FROM Loans AS OldLoan
WHERE EXISTS (
    SELECT 1 FROM Loans AS NewLoan
    WHERE NewLoan.Member_id = OldLoan.Member_id
      AND NewLoan.Loan_id > OldLoan.Loan_id
      AND NewLoan.Annual_interest_rate < OldLoan.Annual_interest_rate
)

此查询将返回至少拥有一对贷款的所有成员的 ID,而后者(按贷款 ID)的利率低于之前的贷款。如果您只想要此类成员的数量,请将DISTINCT Member_id 替换为COUNT(DISTINCT Member_id)

如果内部SELECT 至少匹配一行,则上述查询中的EXISTS 条件评估为真。请注意,内部查询应该返回哪些列并不重要,因为EXISTS 只关心结果是否有任何行,所以我只是告诉它返回包含常量@987654331 的单个列@ 为每个匹配的行。 (无论如何,数据库引擎甚至可能优化掉这个常量。)

你可以把这个查询想象成简单地循环遍历表的每一行,然后在 same 表上再次使用内部循环来查看是否有另一行具有相同的Member_id,更高的Loan_id 和更低的Annual_interest_rate。这可能不是 SQLite 引擎实际最终实现查询的方式,但无论如何你得到的结果都是一样的。


或者,您可以使用Loans 表的self-join 来代替子查询。有几种等效的方法来编写它。例如,您可以在FROM 子句中简单地列出表两次(当然使用不同的别名),然后使用WHERE 子句匹配行,如下所示:

SELECT DISTINCT OldLoan.Member_id
FROM Loans AS OldLoan, Loans AS NewLoan
WHERE NewLoan.Member_id = OldLoan.Member_id
  AND NewLoan.Loan_id > OldLoan.Loan_id
  AND NewLoan.Annual_interest_rate < OldLoan.Annual_interest_rate

(请注意,这里我们需要将结果列显式指定为OldLoan.Member_id,因为OldLoanNewLoan 表——或者更确切地说,同一个表的别名——现在都在外部查询中使用,并且它们都有一个Member_id 列。当然,WHERE 子句确保所有匹配行的成员 ID 都是相同的,但是 SQLite 解析器不够聪明,无法实现这一点。)

对于像这样的简单查询,这种写作风格的连接工作得很好。但是,对于将许多表连接在一起的更复杂的查询,使用显式 JOIN ... ON 子句可能会更整洁,例如像这样:

SELECT DISTINCT OldLoan.Member_id
FROM Loans AS OldLoan
JOIN Loans AS NewLoan ON NewLoan.Member_id = OldLoan.Member_id
WHERE NewLoan.Loan_id > OldLoan.Loan_id
  AND NewLoan.Annual_interest_rate < OldLoan.Annual_interest_rate

并且由于您通过匹配具有相同名称的列来连接两个表(或者更确切地说,在这种情况下,是两个不同名称下的同一个表),您甚至可以使用与USING() 等效的速记语法:

SELECT DISTINCT OldLoan.Member_id
FROM Loans AS OldLoan
JOIN Loans AS NewLoan USING(Member_id)
WHERE NewLoan.Loan_id > OldLoan.Loan_id
  AND NewLoan.Annual_interest_rate < OldLoan.Annual_interest_rate

上面的所有查询都是等价的,因为它们将返回相同的结果,并且最后三个(使用自连接)也应该给出完全相同的执行计划,因此运行速度同样快。子查询版本可能使用不同的执行计划,具体取决于您使用的数据库引擎。在 SQLite 上似乎确实如此,但其他一些数据库可能会同时优化子查询和自联接以在后台以相同的方式执行。

无论如何,要让这些查询在大表上高效运行,您应该至少Member_id 列上有一个索引,以便数据库引擎可以轻松找到对属于同一成员的贷款,而无需扫描表中的每一对行。根据您使用的数据库、表的大小和每个成员的典型贷款数量,其他一些索引可能也很有用,但这已经进入了数据库性能的深奥领域——调音。无论如何,只要没有单个成员的贷款数量多得离谱,只需Member_id 上的简单索引就可以了。

附言。 Here's an SQLFiddle demonstrating all these queries. 您可以使用它来检查它们是否有效,并且它们都返回相同的结果。如果需要,您还可以单击“查看执行计划”以获取有关 SQLite 实际运行这些查询的更多信息。

【讨论】:

  • Ilmari Karonen - 你是最棒的!!!您能否提供有关 SQLite 功能的完整备忘单的建议?请记住,我在这方面非常初学者。非常感谢!
  • @Gabriel:This one 是我在谷歌上搜索“sqlite 备忘单”时得到的第一个结果,它看起来还不错。您可能还想考虑寻找基本的 SQL 教程,可以是在线的,也可以是图书馆的纸质书。 SQL 作为一门语言已经很古老了,对于像这样的基本内容,即使是十到二十年前的书也应该没问题。它不必专门针对 SQLite。
猜你喜欢
  • 1970-01-01
  • 2019-02-17
  • 1970-01-01
  • 1970-01-01
  • 2019-03-12
  • 1970-01-01
  • 2020-03-14
  • 1970-01-01
  • 2022-10-21
相关资源
最近更新 更多