【问题标题】:Find date of most recent overdue查找最近逾期的日期
【发布时间】:2019-06-30 10:50:58
【问题描述】:

我有以下问题:从工资和会费表中,我需要找到最后一次逾期的日期。以下是表格和数据示例:

create table t (
    Id int
  , [date] date
  , Customer varchar(6)
  , Deal varchar(6)
  , Currency varchar(3)
  , [Sum] int
);

insert into t values
  (1, '2017-12-12', '1110', '111111', 'USD', 12000)
, (2, '2017-12-25', '1110', '111111', 'USD', 5000)
, (3, '2017-12-13', '1110', '122222', 'USD', 10000)
, (4, '2018-01-13', '1110', '111111', 'USD', -10100)
, (5, '2017-11-20', '2200', '222221', 'USD', 25000)
, (6, '2017-12-20', '2200', '222221', 'USD', 20000)
, (7, '2017-12-31', '2201', '222221', 'USD', -10000)
, (8, '2017-12-29', '1110', '122222', 'USD', -10000)
, (9, '2017-11-28', '2201', '222221', 'USD', -30000);

如果“Sum”的值为正 - 表示逾期已经开始;如果“总和”为负数 - 表示有人为此交易付款。

在上面的示例中,交易“122222”逾期从 2017 年 12 月 13 日开始,到 2017 年 12 月 29 日结束,因此它不应该出现在结果中。

对于“222221”交易,2017 年 11 月 20 日开始的第一次逾期 25000 已于 2017 年 11 月 28 日完全支付,因此当前逾期的最后日期(我们感兴趣)是 2017 年 12 月- 31

我已经做出这个选择来总结所有付款,并停留在这里:(

WITH cte AS (
    SELECT *,
            SUM([Sum]) OVER(PARTITION BY Deal ORDER BY [Date]) AS Debt_balance
    FROM t
)

显然,如果没有 0 或负的 Debt_balance,我需要找到(对于每个交易)最少的日期,否则在最后一个 0 余额之后的下一个日期..

将不胜感激有关该主题的任何提示和想法。 谢谢!

更新 我的解决方案版本:

WITH cte AS (
    SELECT ROW_NUMBER() OVER (ORDER BY Deal, [Date]) id,
           Deal, [Date], [Sum], 
           SUM([Sum]) OVER(PARTITION BY Deal ORDER BY [Date]) AS Debt_balance
    FROM t
)
SELECT  a.Deal, 
        SUM(a.Sum) AS NET_Debt, 
        isnull(max(b.date), min(a.date)), 
        datediff(day, isnull(max(b.date), min(a.date)), getdate())
FROM cte as a 
LEFT OUTER JOIN cte AS b
ON a.Deal = b.Deal AND a.Debt_balance <= 0 AND b.Id=a.Id+1
GROUP BY a.Deal
HAVING SUM(a.Sum) > 0

【问题讨论】:

  • 要第 4 行和第 7 行吗?
  • 否 :) 我需要第一个 - 因为它是 111111 交易过期的开始,第 6 个 - 222221 交易

标签: sql sql-server tsql sql-server-2014


【解决方案1】:

我相信您正在尝试使用运行总和并跟踪它何时变为正数,并且它可以多次变为正数,并且您想要它变为正数的最后日期。除了运行总和之外,您还需要LAG()

WITH cte1 AS (
    -- running balance column
    SELECT *
         , SUM([Sum]) OVER (PARTITION BY Deal ORDER BY [Date], Id) AS RunningBalance
    FROM t
), cte2 AS (
    -- overdue begun column - set whenever running balance changes from l.t.e. zero to g.t. zero
    SELECT *
         , CASE WHEN LAG(RunningBalance, 1, 0) OVER (PARTITION BY Deal ORDER BY [Date], Id) <= 0 AND RunningBalance > 0 THEN 1 END AS OverdueBegun
    FROM cte1
)
-- eliminate groups that are paid i.e. sum = 0
SELECT Deal, MAX(CASE WHEN OverdueBegun = 1 THEN [Date] END) AS RecentOverdueDate
FROM cte2
GROUP BY Deal
HAVING SUM([Sum]) <> 0

Demo on db<>fiddle

【讨论】:

  • 似乎运作良好,谢谢!我还使用 JOIN 添加了我的版本。第二个表是您代码中的 LAG 用于查找全额还款后的日期
【解决方案2】:

您可以使用窗口函数。这些可以计算中间值:

  • 总和为负数的最后一天(即最后一个“良好”记录)。
  • 最后一笔

然后你可以结合这些:

select deal, min(date) as last_overdue_start_date
from (select t.*,
             first_value(sum) over (partition by deal order by date desc) as last_sum,
             max(case when sum < 0 then date end) over (partition by deal order by date) as max_date_neg
      from t
     ) t
where last_sum > 0 and date > max_date_neg
group by deal;

实际上,最后日期的值是不必要的。所以这简化为:

select deal, min(date) as last_overdue_start_date
from (select t.*,
             max(case when sum < 0 then date end) over (partition by deal order by date) as max_date_neg
      from t
     ) t
where date > max_date_neg
group by deal;

【讨论】:

  • 所以在我的例子中它只给出了一个答案,跳过了第一个交易 (111111),它从一开始就到期并且从未得到完全偿还。我们可能有这些可能性:余额 = 0,仅此而已 - 最后一次付款。余额 = 0,还会有其他付款,可能是其他 0-s。余额永远不会为 0。
  • @DenisKa 。 . .你的问题是“需要找到最后逾期的日期”。如果您有其他问题,请提出 问题。这个问题似乎很清楚。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多