【问题标题】:SQL Query: Finding Overdue Start Date and Overdue Amount from a tableSQL查询:从表中查找逾期开始日期和逾期金额
【发布时间】:2018-03-01 14:10:42
【问题描述】:

我有一个如下表,其中有一个“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 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 的总和(会费)是 2000,并且 C(集合)只有 500,所以 2000 - 500 = 1500 是到期的,因为 未履行2017年1月10日全额付款,逾期开始 日期仅为 2017 年 1 月 10 日。

  • 假设如果我们取 LoanID 12346,D(Dues) 的总和是 3500,C (collection) 是 2500,所以逾期金额是 3500 - 2500 = 1000 和 逾期开始日期是 01/01/18,因为它没有履行到期日期 还没有。

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

感谢任何帮助。

【问题讨论】:

  • if we take LoanID 12345, The Overdue start date is: 01/10/2017 and overdue Amount is: 1500 - 你如何计算 1500? 12345 有 3 条记录,但没有一条是 1500。12346 看起来更复杂。
  • @Shawn,感谢您的回复,我已在 UPDATE 下给出了解释。请通过它。
  • 所以逾期时间本质上就是DD - C = 0的时间?
  • 为什么C 有过期日期? OverDueDate真的是交易日吗?
  • 什么类型的 SQL 服务器?微软、甲骨文、MySQL、Postgre,还有什么?

标签: sql zoho


【解决方案1】:

这是 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 */
;  

我在数据中添加了几行额外的行来展示一些变化,例如多付或还清贷款。我还更改了一些日期的顺序,以显示OVER() 窗口函数中的ORDER BY 如何纠正无序数据。

查询注意: 我评论了 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) |

【讨论】:

  • 呸。执行计划是丑陋的。如果它运行在很多行上或定期运行,我相信它可以被拆除和优化。
  • 感谢您的解决方案,但它对我不起作用,因为我的 SQL 查询无法使用 LAG、OVER、PARTITION BY 关键字,它仅限于编写查询。它只允许 LEFT、RIGHT、INNER JOIN、Group by 和 Order by。
  • 据我所知,Zoho 不使用自己的数据库。它使用您设置的一个。有了这样简单的 SQL 结构,我不确定您是否能理解您想要理解的内容。您可能需要一直返回数据库并创建视图。你知道 Zoho 使用的是什么类型的数据库吗?
猜你喜欢
  • 1970-01-01
  • 2019-06-30
  • 2020-09-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-23
  • 2021-03-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多