SQL查询:从表中查找过期开始日期和过期金额

问题描述 投票:1回答:1

我有一个如下表,其中有一个'ODType'列,该列表明一个事务是Due(D)或Collection-ed(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 12345,则逾期开始日期为:01/10/2017,逾期金额为:1500
  • 如果我们采用LoanID 12346,则逾期开始日期为:01/01/2018,逾期金额为:1000

我能够获得每个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。
  • 假设我们采用LoanID 12346,D(Dues)之和为3500而C(Collection)为2500,那么逾期金额为3500 - 2500 = 1000且逾期开始日期为01/01/18,因为它没有到期日期。

注意:这需要通过简单的JOIN或LEFT或RIGHT或Inner JOIN查询来实现。不适用于Partition,LAG,OVER和row_Number关键字,这意味着这些内置函数不可用于编写查询。

感谢任何帮助。

sql zoho
1个回答
0
投票

这是Microsoft T-SQL语法,根据您的服务器语言,它可能会有所不同。我使用MS-SQL的LAG()函数,它是在MS SQL 2012中引入的。所有概念都应该可以转换为您正在使用的任何SQL风格。

SQL Fiddle

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) |
© www.soinside.com 2019 - 2024. All rights reserved.