Расчет максимального значения с течением времени - PullRequest
0 голосов
/ 02 марта 2020

Итак, у меня есть три таблицы на сервере MS SQL:

  • Резервы (ReserveID, ProjectID, TypeID, DateCreated, Current (двоичный), Сумма)
  • Платежи ( PaymentID, ProjectID, TypeID, DateCreated, Status, Amount)
  • Ссылки (LinkID, ReserveID, PaymentID)

Тип распределяет платежи / резерв по таким вещам, как строительные материалы, арматура, трудозатраты , профессиональные услуги и т. д. c

Таким образом, при запуске проекта будет создан ряд резервов на основе ожидаемых затрат.

Вполне возможно, что вы изначально догадались, что материалы собираются стоит 5000 долларов, но затем, сделав несколько запросов, вы понимаете, что это будет больше похоже на 10 000 долларов, и в этом случае устанавливается новый резерв на 10 000 долларов, а «текущий» статус в таблице исходного элемента устанавливается равным 0

. Когда счет поступает и оплачивается, он попадает в таблицу платежей, и в третью таблицу вводится связь между резервом и платежом. Когда он представляется пользователю во внешнем интерфейсе, платеж вычитается из резерва, но в бэкэнде таблица резервов остается неизменной. Например, если для Материалов есть резерв в размере 10 000 долларов США и оплачен счет на материалы в размере 2 500 долларов США, то внутренний сервер все равно будет иметь резерв в 10 000 рублей и платеж в размере 2,5 000 долларов США, но на внешнем интерфейсе будет отображаться резерв в размере 7 000 000 долларов США, 2,5 000 долларов США и общая сумма 10 000 долларов США.

Если платеж превышает резерв для типа, тогда передний конец покажет резерв в размере 0, 12 тыс. Платежей и 12 тыс. Всего. (Резервы для любых других типов останутся нетронутыми - ie, если материал go 5k сверх бюджета, тогда трудовой резерв не уменьшается).

Если резерв изменяется после платежа (что приводит к новому строка в таблице Резерв), после чего внешний интерфейс прекращает вычитать из него предыдущие платежи. Таким образом, в предыдущем примере внешний интерфейс отображал резерв 7,5 тыс., Платеж 2,5 тыс. И итого 10 тыс. Если пользователь затем изменит резерв на 10 КБ, то на внешнем интерфейсе отобразится резерв 10 КБ, 2,5 К платежа и 12,5 К платежа.

Это плохой способ ведения дел, но я не могу изменить интерфейс.

Поэтому мне нужно рассчитать, какой был Максимальный итог для каждого проекта в любой момент времени. В идеальном мире это было бы текущее положение, но возможно, что вещи были переоценены в прошлом, и тогда резерв был уменьшен, когда было обнаружено, что что-то может быть восстановлено, а не заменено et c.

Я Затрудняюсь сказать, как это сделать вообще, не говоря уже об эффективном способе работы с довольно большим набором данных.

Большое спасибо за вашу помощь.

Образец данных

Резерв

ReserveID ProjectID TypeID  DateCreated Current Amount

23  64  4   03/01/2020  0   5,000.00 
24  65  3   03/01/2020  1   1,000.00 
25  64  4   05/01/2020  1   10,000.00 
26  64  1   08/01/2020  1   500.00 
27  66  1   09/01/2020  1   750.00 
28  64  3   10/01/2020  1   250.00 
29  68  3   10/01/2020  0   20,000.00 
30  68  3   11/01/2020  1   5,000

Оплата

PaymentID   ProjectID   TypeID  DateCreated Status  Amount

87  64  4   08/01/2020  Declined    10,000.00 
88  64  8   09/01/2020  Approved    1,000.00 
89  71  2   10/01/2020  Approved    5,000.00 
90  66  1   12/01/2020  Approved    700.00 

Ссылки

LinksID ReserveID   PaymentID

21  25  87
22  25  88
23  27  90

Желаемый выход

ProjectID   MaxPotentialCost

64   11,750.00 
65   1,000.00 
66   750.00 
68   20,000.00 
71   5,000.00 

1 Ответ

0 голосов
/ 02 марта 2020

Хотя мне не совсем ясно, что вы хотите (пример не очень убедительный или законченный) Я могу привести вам рабочий пример, который по крайней мере (надеюсь) охватывает методологию, которую вы, кажется, хотите.

(я собираюсь составить свои собственные данные, чтобы более наглядно показать, что я имею в виду ...)

ProjectID  TypeID  EffectiveDate  Reserve

64         4       01/01/2020      1,000
64         4       07/01/2020      5,000
64         4       11/01/2020      1,000

64         5       12/01/2020      1,000


ProjectID  TypeID  EffectiveDate  Payment

64         4       03/01/2020      1,000
64         4       05/01/2020      1,000
64         4       09/01/2020      2,000

64         5       10/01/2020      3,000

Первым делом может показаться, что все получается в одном наборе временных рядов, а не в двух. Тогда есть текущая сумма платежей на сегодняшний день, а не отдельные суммы. Это упрощает расчет экспозиции («общее», на которое вы ссылаетесь) для каждого (ProjectId,TypeID).

PID,TID   EffectiveDate   Reserve   Payment   NewSpent   OldSpent   Exposure

64,4      01/01/2020        1,000         0         0         0     1,000   -- Reserve Added
64,4      03/01/2020        1,000     1,000     1,000         0     1,000   -- Payment Made, so newSpent increases
64,4      05/01/2020        1,000     1,000     2,000         0     2,000   -- Payment Made, so newSpent increases
64,4      07/01/2020        5,000         0         0     2,000     7,000   -- Reserve Revised, newSpent is added to OldSpent and then resets to zero 
64,4      09/01/2020        5,000     2,000     2,000     2,000     7,000   -- Payment Made, so newSpent increases
64,4      11/01/2020        1,000         0         0     4,000     5,000   -- Reserve Revised, newSpent is added to OldSpent and then resets to zero 

64,5      10/01/2020            0     3,000     3,000         0     3,000   -- Payment Made, so newSpent increases
64,5      12/01/2020        1,000         0         0     3,000     4,000   -- Reserve Revised, newSpent is added to OldSpent and then resets to zero 

Пример SQL:

WITH
  combined AS
(
  SELECT
    payments.ProjectID,
    payments.TypeID,
    links.ReserveID,
    payments.DateCreated,
    COALESCE(reserve.Amount, 0)   AS reserve,
    payments.Amount               AS spent
  FROM
    payments
  LEFT JOIN
    links
      ON payments.PaymentID = links.PaymentID
  LEFT JOIN
    reserve
      ON reserve.ReserveID  = links.ReserveID        
  WHERE
    payments.status = 'Approved'

  UNION ALL

  SELECT
    reserve.ProjectID,
    reserve.TypeID,
    reserve.ReserveID,
    reserve.Amount,
    0
  FROM
    reserve
)
,
  runningTotal AS
(
  SELECT
    ProjectID,
    TypeID,
    DateCreated,
    reserve,
    SUM(spent)
      OVER (PARTITION BY ProjectID, TypeID, ReserveID
                ORDER BY DateCreated
           )
             AS spentReserve,
    SUM(spent)
      OVER (PARTITION BY ProjectID, TypeID
                ORDER BY DateCreated
           )
             AS spentProjectType
  FROM
    combined
)
,
  exposure AS
(
  SELECT
    ProjectID,
    TypeID,
    DateCreated,
    reserve,
    spentReserve                      AS spentNew,
    spentProjectType - spentReserve   AS spentOld,
    CASE WHEN
      spentReserve > reserve  -- If new spending exceeds the current reserve
    THEN                      -- The the exposure equals total spending to date
      spentProjectType        -- Otherwise it's the old spent value, plus the reserve
    ELSE
      spentProjectType - spentReserve + reserve
    END
                                      AS exposure
  FROM
    runningTotal
)

На этом этапе я бы свел все это к тому, насколько изменилась «Экспозиция», поскольку это упростит чередование нескольких типов.

PID,TID   EffectiveDate   Exposure   Delta

64,4      01/01/2020      1,000      1,000   -- Reserve Added
64,4      03/01/2020      1,000          0   -- Payment Made
64,4      05/01/2020      2,000      1,000   -- Payment Made
64,4      07/01/2020      7,000      5,000   -- Reserve Revised
64,4      09/01/2020      7,000          0   -- Payment Made
64,4      11/01/2020      5,000     -2,000   -- Reserve Revised

64,5      10/01/2020      3,000      3,000   -- Payment Made
64,5      12/01/2020      4,000      1,000   -- Reserve Revised

Пример SQL :

,
  deltaExposure AS
(
  SELECT
    exposure.*,
    exposure.exposure
    -
    LAG(exposure.exposure, 0)
      OVER (PARTITION BY exposure.ProjectID, exposure.TypeID
                ORDER BY exposure.DateCreated
           )
             AS deltaExposure
  FROM
    exposure
)

Затем можно чередовать несколько типов по времени, и значения DeltaExposure суммируются по всему проекту ...

PID,TID   EffectiveDate   Delta   ProjectExposure

64,4      01/01/2020      1,000      1,000   -- Reserve Added
64,4      03/01/2020          0      1,000   -- Payment Made
64,4      05/01/2020      1,000      2,000   -- Payment Made
64,4      07/01/2020      5,000      7,000   -- Reserve Revised
64,4      09/01/2020          0      7,000   -- Payment Made
64,5      10/01/2020      3,000     10,000   -- Payment Made
64,4      11/01/2020     -2,000      8,000   -- Reserve Revised
64,5      12/01/2020      1,000      9,000   -- Reserve Revised

Пример SQL:

,
  projectExposure AS
(
  SELECT
    deltaExposure.*,
    SUM(deltaExposure.exposure)
      OVER (PARTITION BY deltaExposure.ProjectID
                ORDER BY deltaExposure.DateCreated
           )
             AS projectExposure
  FROM
    deltaExposure
)
SELECT
  ProjectID,
  MAX(ProjectExposure)   AS MaxProjectExposure
FROM
  projectExposure
GROUP BY
  ProjectID

Пример, показывающий, что необходим расчет изо дня в день:

Представьте проект, состоящий из двух частей Exp1 и Exp2. После расчета рисков на каждую дату события (каждый платеж или пересмотр резерва) мы получаем следующие значения «DeltaExposure».

Date   01  02  03  04  05
Exp1   +2  +2  +2  +2       -- Reserve set to 2, then payments of 4, 2 and 2
Exp2   +6          -4  +1   -- Reserve set to 6, later revised to 2, then a payment of 3

TOTAL   8  10  12  10  11

Пик воздействия приходится на дату 2-го платежа на Exp1. Средний платеж три; нет никакого способа узнать, какие Даты платежа из Exp1 являются кандидатами на пиковую подверженность без учета всех других временных рядов ...

...