Найти дату последнего просроченного - PullRequest
0 голосов
/ 06 февраля 2019

У меня следующая проблема: из таблицы платежей и сборов мне нужно найти дату последнего просроченного платежа.Вот таблица и данные, например:

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);

Если значение «Сумма» положительное - это означает, что просрочено началось;если "Сумма" отрицательна - это означает, что кто-то заплатил по этой сделке.

В приведенном выше примере по сделке "122222" просроченная сумма начинается в 2017-12-13 и заканчивается 2017-12-29, поэтомув результате не будет.

А по сделке '222221' первый просроченный платеж 25000, начатый в 2017-11-20, был полностью оплачен в 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

Ответы [ 2 ]

0 голосов
/ 06 февраля 2019

Я полагаю, что вы пытаетесь использовать текущую сумму и отслеживать, когда она меняется на положительную, и она может многократно меняться на положительную, и вам нужна последняя дата, когда она стала положительной.Вам нужно 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

Демонстрация на db <> fiddle

0 голосов
/ 06 февраля 2019

Вы можете использовать оконные функции.Они могут вычислять промежуточные значения:

  • Последний день, когда сумма является отрицательной (т. Е. Последняя «хорошая» запись).
  • Последняя сумма

Тогда выможно объединить их:

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;
...