SQL с использованием результатов из предыдущей строки

Я пытаюсь выполнить расчет в формате Excel в SQL. Это включает использование коэффициента закрытия (ClRate) из предыдущей строки и его использование для вычисления значений в следующей строке. Таблица начинается с 1 января и содержит 1000 строк, и каждая строка содержит известные данные и данные, которые необходимо вычислить (показано в []).

Date     RecQty   RecAmt     IssQty        IssAmt       ClQty        ClAmt          ClRate
1 Jan       -        -           -           -           100         $20,000          200
2 Jan      +10      +$2100       -5         [ ]          [  ]         [  ]            [ ]

Расчеты для получения желаемого результата приведены в таблице ниже.

Date     RecQty   RecAmt     IssQty        IssAmt       ClQty        ClAmt             ClRate
1 Jan                                                    100         $20,000             200
2 Jan      +10      +$2100       -5       -[200*5]   [100+10-5]  [20,000+2100-200*5]  [ClAmt/ClQty]

IssAmt для каждого дня будет рассчитываться путем умножения IssQty на ClRate предыдущих дней. ClQty рассчитывается как ClQty предыдущего дня + RecQty текущего дня - IssQty текущего дня. ClAmt рассчитывается как ClAmt предыдущего дня + RecAmt текущего дня - IssAmt текущего дня. Наконец, ClRate для каждого дня рассчитывается как ClAmt / ClQty.

Единственный известный ClRate - это начальная инвентарная строка таблицы (1 января) - после этого необходимо вычислить ClRate для каждой последующей строки.

В Excel вы просто выполните этот расчет, связав необходимые ячейки предыдущей строки и скопировав / вставив формулу во все строки ниже.

Как бы вы это сделали в SQL? Я пробовал самостоятельно присоединяться к CTE, циклам и LAG - ни один из них, похоже, не работает. Причина в том, что значение ClRate для каждой строки, начиная со 2 января, неизвестно - и хотя Excel может «на лету» обрабатывать результаты вычислений, которые используются в следующей строке, SQL не может этого сделать.

Обращаюсь за помощью в решении этой проблемы. Я использую SQL Server 2017 и SSMS. При необходимости могу предоставить код

Таблица DDL

CREATE TABLE [Auto].[IronOreTbl](
    [Id] [int] NULL,
    [Counter] [int] NULL,
    [TDate] [date] NOT NULL,
    [RecQty] [decimal](16, 2) NULL,
    [RecAmt] [decimal](16, 2) NULL,
    [IssQty] [decimal](16, 2) NULL,
    [IssAmt] [decimal](16, 2) NULL,
    [ClQty] [decimal](16, 2) NULL,
    [ClAmt] [decimal](16, 2) NULL,
    [ClRate] [decimal](16, 2) NULL

) ON [PRIMARY]
GO

INSERT INTO [Auto].[IronOreTbl]
    ([Id],[Counter],[TDate],[RecQty],[RecAmt],[IssQty],[IssAmt],[ClQty],[ClAmt],[ClRate])
    VALUES
    (1,0,'2019-01-01',NULL,NULL,NULL,NULL,100,20000,200),
    (2,1,'2019-01-02',10,2100,5,NULL,105,NULL,NULL),
    (3,2,'2019-01-03',8,1600,2,NULL,111,NULL,NULL),
    (4,3,'2019-01-04',20,2400,10,NULL,121,NULL,NULL)

Попытки CTE

;WITH ClAmtCTE AS

    (
    SELECT
        Id,RecQty,RecAmt,IssQty,ClQty,ClAmt,ClRate
        ,EffRate = ClRate
        ,CumHoldVal= ClAmt
        --CAST(ClAmt AS decimal(16,2))
        ,CumClRt=CAST(ClRate AS decimal(16,2))
        ,[Counter]
    FROM
        [Auto].IronOreTbl
    WHERE
        Id=1 

    UNION ALL

    SELECT
        C2.Id,C2.RecQty,c2.RecAmt,C2.IssQty,C2.ClQty,C2.ClAmt,c2.ClRate,
        EffRate = (SELECT CumClRt WHERE C2.ID=C2.[Counter]+1), 
        CumRN = 
        CAST(
            (
            CumHoldVal+ISNULL(C2.RecAmt,0)-
            (EffRate)*ISNULL(C2.IssQty,0)
            )
        AS decimal(16,2)
        ),
        CumClRt=CAST(CumHoldVal/C2.ClQty AS decimal(16,2)),
        C2.[Counter],

    FROM
        [Auto].IronOreTbl C2 
    INNER JOIN ClAmtCTE C1 ON C1.Id = C2.[Counter]

person Mangésh Méhendalé    schedule 04.05.2020    source источник
comment
Вы отметили CTE, и это на правильном пути. Как выглядит ваша попытка запроса CTE?   -  person devlin carnate    schedule 04.05.2020
comment
Я добавляю таблицу DDL и CTE в сообщение   -  person Mangésh Méhendalé    schedule 04.05.2020


Ответы (1)


Следующий код дает желаемый результат - хотя я подошел ближе несколько дней назад, все это время потребовалось, чтобы связать последний бит; сопоставления ClRate с правильной строкой. Возникла дополнительная проблема, когда в дни, когда нет проблем, а есть только квитанции, полученная ставка принадлежала неправильной строке (я до сих пор не знаю, почему и как это происходило - все, что я знаю, это то, что требовался предыдущий код. пересмотр для устранения этого несоответствия, и измененный код относится к тому же)

;WITH ClAmtCTE AS

                (
                SELECT
                    Id,RecQty,RecAmt,IssQty,ClQty,ClAmt,ClRate
                    ,EffRate = ClRate
                    ,CumHoldVal= ClAmt
                    --CAST(ClAmt AS decimal(16,2))
                    ,CumClRt=CAST(ClRate AS decimal(16,2))
                    ,[Counter]
                FROM
                    [Auto].IronOreTbl
                WHERE
                    Id=1 

                UNION ALL

                SELECT
                    C2.Id,C2.RecQty,c2.RecAmt,C2.IssQty,C2.ClQty,C2.ClAmt,c2.ClRate,
                    EffRate = (SELECT CumClRt WHERE C2.ID=C2.[Counter]+1), 
                    CumRN = 
                    CAST(
                        (
                        CumHoldVal+ISNULL(C2.RecAmt,0)-
                        ((SELECT CumClRt WHERE C2.ID=C2.[Counter]+1))*ISNULL(C2.IssQty,0)
                        )
                    AS decimal(16,2)
                    ),
                    CumClRt=CAST((CumHoldVal+ISNULL(C2.RecAmt,0)-
                        ((SELECT CumClRt WHERE C2.ID=C2.[Counter]+1))*ISNULL(C2.IssQty,0))/C2.ClQty AS decimal(16,2)),
                    C2.[Counter]

                FROM
                    [Auto].IronOreTblC2 
                INNER JOIN ClAmtCTE C1 ON C1.Id = C2.[Counter]
                )

                UPDATE [Auto].IronOreTbl
                SET ClRate = T1.CumClRt,
                ClAmt=CumHoldVal
                FROM ClAmtCTE T1
                INNER JOIN [Auto].IronOreTbl T2
                ON T1.ID = T2.Id
                OPTION (MAXRECURSION 0) 

Попытка и, как мне кажется, ее решение научили меня нескольким вещам. В первую очередь к ним относятся:

  1. Вычисления, которые вы можете настроить и запустить в мгновение ока в электронной таблице, - это что угодно, только не в SQL.

  2. В Excel есть возможность производить вычисления «на лету» и использовать результаты, рассчитанные «на лету», в зависимых вычислениях. SQL не может этого сделать. Наличие этой возможности в Excel означает, что вам не нужно перебирать данные - один раз, чтобы Excel вычислил результаты, а затем Excel, чтобы применить результаты там, где они необходимы.

  3. Несмотря на все свое использование и полезность в качестве среды для хранения данных, SQL предстоит пройти долгий путь в отношении стандартных вычислений реального мира - таких, как этот пример; множественные ссуды, изменение процентных ставок и соответствующих расчетов процентов и т. д. и т. д.

person Mangésh Méhendalé    schedule 06.05.2020