我有一张如下表格,其中有一个' ODType'列,此列说明事务是到期(D)或收集(C)金额。由此我需要找出每笔贷款的逾期开始日期和逾期金额。
LoanID OverDueDate TotalAmount ODType
12345 01/10/17 1000 D
12345 01/11/17 500 C
12345 03/12/17 1000 D
12346 01/10/17 1500 D
12346 01/11/17 500 C
12346 03/12/17 1000 C
12346 01/01/18 2000 D
12346 01/02/18 1000 C
示例场景:
我能够获得每个loanId的逾期金额,但不知道如何获得逾期开始日期。我用以下查询做到了:
SELECT t.LoanID, (t."DemandAmount" -t."CollectionAmount") Overdue
FROM (SELECT
LoanID,
MAX(CASE
WHEN ODType = 'D' THEN ("TotalAmount")
END) AS DemandAmount,
MAX(CASE
WHEN (ODType = 'C') THEN ("TotalAmount")
END) AS CollectionAmount
FROM TXN_OverdueCollection GROUP BY LoanID ) t
如何找出逾期开始日期,我需要添加哪些额外条件才能将其与逾期金额区分开来。或者我是否需要完全更改查询以获得逾期开始日期和逾期金额。
更新 逾期金额和逾期开始日期计算信息如下:
逾期金额来自会费总额(D)减去收款总数(C)。
假设我们采用LoanID 12345,D(Dues)之和为2000且 C(收藏)只有500,所以2000 - 500 = 1500是到期和之后 它不符合01/10/2017全额付款,即逾期开始 日期仅为01/10/2017。
注意: 这需要通过简单的JOIN或LEFT或RIGHT或Inner JOIN查询来实现。不适用于Partition,LAG,OVER和row_Number关键字,这意味着这些内置函数不可用于编写查询。
感谢任何帮助。
答案 0 :(得分:0)
这是Microsoft T-SQL语法,根据您的服务器语言,它可能会有所不同。我使用MS-SQL的LAG()
函数,它是在MS SQL 2012中引入的。所有概念都应该可以转换为您正在使用的任何SQL风格。
MS SQL Server 2017架构设置:
CREATE TABLE t ( LoanID int, OverDueDate date, TotalAmount decimal(10,2), ODType varchar(1));
INSERT INTO t ( LoanID, OverDueDate, TotalAmount, ODType )
VALUES
(12345, '01/10/17', 1000, 'D')
, (12345, '01/11/17', 500, 'C')
, (12346, '02/10/17', 1500, 'D')
, (12346, '03/12/17', 1000, 'C') /* Paid off. But more loans. */
, (12346, '01/02/18', 1000, 'C')
, (12345, '03/12/17', 1000, 'D') /* Additional deposit. Maintains original overdue date */
, (12346, '02/11/17', 500, 'C')
, (12346, '01/01/18', 2000, 'D')
, (12347, '10/01/17', 1000, 'D')
, (12347, '11/01/17', 1001, 'C') /* Overpaid */
, (12348, '11/11/17', 1000, 'D')
, (12348, '12/11/17', 1000, 'C') /* Paid off */
;
我在数据中添加了几行以展示一些变化,例如超额付款或还清贷款。我还更改了某些日期的顺序,以显示ORDER BY
窗口函数中的OVER()
如何更正无序数据。
查询: 注意: 我评论了SQL来解释我所做的一些事情。
; WITH cte1 AS ( /* Created CTE because use this query in main and sub query. */
SELECT s1.LoanID
, s1.OverDueDate
, s1.TotalAmount
, s1.ODType
, s1.runningTotal
, CASE
WHEN (
COALESCE ( /* COALESCE() will handle NULL dates. */
LAG(s1.runningTotal) /* LAG() is SQL2012. */
OVER ( PARTITION BY s1.LoanID ORDER BY s1.LoanID, s1.OverDueDate )
, 0 ) <= 0
/* This resets the OverDueDate. "<=0" will reset date for overpays. */
) THEN s1.OverDueDate
ELSE NULL
END AS od
, s1.rn
FROM (
SELECT t.LoanID
, t.OverDueDate
, t.TotalAmount
, t.ODType
, SUM( CASE
WHEN t.ODType = 'D' THEN t.TotalAmount
WHEN t.ODType = 'C' THEN t.TotalAmount*-1
ELSE 0
END )
OVER (
PARTITION BY LoanID
ORDER BY OverDueDate
) AS runningTotal
/* We need to be able to calculate + (D) and - (C) to get a running total. */
, ROW_NUMBER() OVER ( PARTITION BY t.LoanID ORDER BY t.OverDueDate DESC ) AS rn
/* ROW_NUMBER() helps us find the most recent record for the LoanID. */
FROM t
) s1
)
SELECT b.LoanID
, b.TotalAmount
, b.ODType
, b.runningTotal
, CASE
WHEN b.od IS NOT NULL THEN b.od
WHEN b.runningTotal <= 0 THEN NULL /* If they don't owe, they aren't overdue. */
ELSE ( SELECT max(s1.od)
FROM cte1 s1
WHERE b.LoanID = s1.LoanID
AND s1.OverDueDate <= b.OverDueDate
)
END AS runningOverDue /* Calculate the running overdue date. */
FROM cte1 b
WHERE b.rn=1 /* rn=1 gets the most recent record for each LoanID. */
AND b.runningTotal <> 0 /* This will exclude anyone who doesn't currently
owe money but did. Change to >0 to include only overdues. */
ORDER BY b.LoanID, b.OverDueDate
<强> Results 强>:
| LoanID | overduedate | TotalAmount | ODType | runningTotal | runningOverDue |
|--------|-------------|-------------|--------|--------------|----------------|
| 12345 | 2017-03-12 | 1000 | D | 1500 | 2017-01-10 |
| 12346 | 2018-01-02 | 1000 | C | 1000 | 2018-01-01 |
| 12347 | 2017-11-01 | 1001 | C | -1 | (null) |