【问题标题】:Find out the date of the current debt in SQL在 SQL 中找出当前债务的日期
【发布时间】:2021-08-25 15:21:51
【问题描述】:

请帮我弄清楚如何找出当前债务的日期和自开始以来的天数我有这张表:

Date Customer Deal Sum
20.11.2009 220000 222221 25000
27.11.2009 220001 222221 -30000
20.12.2009 220000 222221 20000
31.12.2009 220001 222221 -10000
12.12.2009 111110 111111 12000
25.12.2009 111110 111111 5000
12.01.2010 111110 111111 -10100
12.12.2009 111110 122222 10000
29.12.2009 111110 122222 -10000

在贷款中,共同借款人可以付款。如果有贷款的客户未能按时还款,他就有了债务。在这种情况下,表格中会出现一条相应的记录,其中 Sum 是未付金额(带有正号)。然后,如果客户付款(全部或部分),则会出现一条新记录,其中 Sum 是已支付的金额(带有“-”号)。需要注意的是,客户的付款并不一定会完全消除累积的债务,它只能是债务的一部分。

DROP TABLE IF EXISTS #PDCL
set dateformat dmy
CREATE TABLE #PDCL
(
Payment_dt date,
Customer int,
Deal int,
Currency varchar(5),
Sum_payment int
)
INSERT INTO #PDCL VALUES ('12.12.2009', 111110, 111111, 'RUR', 12000)
INSERT INTO #PDCL VALUES ('25.12.2009', 111110, 111111, 'RUR', 5000)
INSERT INTO #PDCL VALUES ('12.12.2009', 111110, 122222, 'RUR', 10000)
INSERT INTO #PDCL VALUES ('12.01.2010', 111110, 111111, 'RUR', -10100)
INSERT INTO #PDCL VALUES ('20.11.2009', 220000, 222221, 'RUR', 25000)
INSERT INTO #PDCL VALUES ('20.12.2009', 220000, 222221, 'RUR', 20000)
INSERT INTO #PDCL VALUES ('31.12.2009', 220001, 222221, 'RUR', -10000)
INSERT INTO #PDCL VALUES ('29.12.2009', 111110, 122222, 'RUR', -10000)
INSERT INTO #PDCL VALUES ('27.11.2009', 220001, 222221, 'RUR', -30000)

--Start date of the current debt
SELECT Deal
     , MIN(Payment_dt) AS Start_date_current_debt
FROM #PDCL
WHERE Sum_payment > 0
GROUP BY Deal

--Number of days of current debt
SELECT Deal
     , DATEDIFF(d, MIN(Payment_dt), MAX(Payment_dt)) AS Num_days_current_debt
FROM #PDCL
GROUP BY Deal

数据集有许多不同的客户和交易。我举了一个说明性的例子,因此出现了问题。在其中,客户有两倍的债务。

我想要的答案:

Deal Start_date_current_debt
111111 2009-12-12
122222 2009-12-12
222221 2009-12-20
Deal Num_days_current_debt
111111 todate - 2009-12-12
122222 17
222221 todate - 2009-12-20

【问题讨论】:

  • 感谢您发布创建表和示例数据的脚本 - 这是提出 SQL 问题的完美方式!

标签: sql sql-server tsql


【解决方案1】:

在阅读了有关此答案的 cmets 后,这是一种解决所提问题的方法。我采用了一种稍微冗长的方法,以便您可以遵循逻辑,但可以随意折叠一些公用表表达式以使其更短。

我们可以计算每笔交易的运行SUM,我将为每笔交易的行编号。然后,我们可以使用LAG 将当前交易行的SUM 与交易前一行的SUM 进行比较。当SUM从负数变为正数,或者和为正数而之前的SUMNULL时,我们发现了债务交叉的地方。在这些情况下,我会将行号乘以 -1,以便我可以找到每笔交易的 MIN 行号,这将是欠款的最近日期。正如我所提到的,这可以缩短,但我让它有点冗长,所以你可以按照逻辑:

;WITH sums AS (
    SELECT Deal, 
        Payment_Dt, 
        SUM(Sum_payment) OVER (PARTITION BY Deal ORDER BY Payment_dt) AS [currentSum], 
        ROW_NUMBER() OVER (PARTITION BY Deal ORDER BY Payment_dt)  AS [num]
    FROM #PDCL
), sumsWithLag AS (
    SELECT Deal, Payment_dt, 
    currentSum,  
    LAG(currentSum) OVER (PARTITION BY Deal ORDER BY Payment_dt)  AS [prevSum], 
    num
    FROM sums
), markedCrossings AS (
    SELECT Deal, Payment_dt, 
    CASE WHEN currentSum > 0 AND (prevSum IS NULL OR prevSum < 0) THEN -1 ELSE 1 END * num AS num
    FROM sumsWithLag
), debtCrossings AS (
    SELECT Deal, MIN(num) AS num 
    FROM markedCrossings 
    GROUP BY Deal
)
SELECT s.Deal, s.Payment_dt AS Start_date_current_debt
FROM debtCrossings AS c 
INNER JOIN sums AS s ON s.Deal = c.Deal and s.num = ABS(c.num)

它给出了这个结果:

Deal Start_date_current_debt
111111 2009-12-12
122222 2009-12-12
222221 2009-12-20

这些是预期值。此时,我们可以使用相同的公用表表达式来回答负债天数。我们知道开始日期,所以我们只需要查看交易的最近金额是否为正。

;WITH sums AS (
    SELECT Deal, 
        Payment_Dt, 
        SUM(Sum_payment) OVER (PARTITION BY Deal ORDER BY Payment_dt) AS [currentSum], 
        ROW_NUMBER() OVER (PARTITION BY Deal ORDER BY Payment_dt)  AS [num]
    FROM #PDCL
), sumsWithLag AS (
    SELECT Deal, Payment_dt, 
    currentSum,  
    LAG(currentSum) OVER (PARTITION BY Deal ORDER BY Payment_dt)  AS [prevSum], 
    num
    FROM sums
), markedCrossings AS (
    SELECT Deal, Payment_dt, 
    CASE WHEN currentSum > 0 AND (prevSum IS NULL OR prevSum < 0) THEN -1 ELSE 1 END * num AS num
    FROM sumsWithLag
), debtCrossings AS (
    SELECT Deal, MIN(num) AS num 
    FROM markedCrossings 
    GROUP BY Deal
), startDates AS (
    SELECT s.Deal, s.Payment_dt AS Start_date_current_debt
    FROM debtCrossings AS c 
    INNER JOIN sums AS s ON s.Deal = c.Deal and s.num = ABS(c.num)
), balances AS (
    SELECT Deal, SUM(Sum_payment) AS balance, MAX(Payment_dt) AS Payment_dt
    FROM #PDCL 
    GROUP BY Deal
) 
SELECT s.Deal, 
DATEDIFF(day, s.Start_date_current_debt, CASE WHEN b.balance > 0 THEN GETDATE() ELSE b.Payment_dt END) AS Num_days_current_debt
FROM startDates AS s 
INNER JOIN balances AS b ON s.Deal = b.Deal;

结果是:

Deal Num_days_current_debt
111111 4274
122222 17
222221 4266

【讨论】:

  • 我发现一个错误。 222221 是 Start_date_current_debt 20.12.2009 而不是 20.11.2009。那是我的问题
  • @Andrey 所以你的意思是 start_date_current_debt 不是贷款/交易开始的时间,而是客户第一次因任何贷款而负债的时间?您提到贷款也可​​以有共同借款人,您将如何处理这种情况?
  • 当前债务是债务产生日期和完全偿还日期之间的差额。 222221案于2009年11月27日,债务已全部清偿,2009年12月20日,客户再次陷入债务。 start_date_current_debt 为 222221 - 2009 年 12 月 20 日。 222221 的 Num_days_current_debt - 今天 - 2009 年 12 月 20 日
  • 共同借款人没有强制性的支付优先权。只是交易的参与者可以不止1个。
  • @Andrey 我将更新第一个查询以执行您现在解释的操作
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-09
  • 2020-11-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多