使用前一行结果的SQL语句

问题描述 投票:1回答:1

我试图在SQL中做一个类似Excel的计算,这涉及到使用前一行的收盘率(ClRate)并使用它来计算下一行的值。 这涉及到使用前一行的收盘率(ClRate),并使用它来计算下一行的值。 该表从1月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月1日)--此后每行的ClRate需要计算。

在Excel中,你可以简单地通过连接前一行所需的单元格,并将公式复制粘贴到下面的所有行中来进行计算。

在SQL中,你是如何做到这一点的?我试过自连接CTE,循环和LAG--这些似乎都不行。 原因是从1月2日开始每行的ClRate不知道--虽然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]
sql sql-server common-table-expression sql-server-2017 self-join
1个回答
0
投票

下面的代码达到了预期的结果--虽然我在几天前就已经很接近了,但还是花了这么多时间来完成最后一点;将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作为数据存储媒介的用途和作用,但在参加标准的现实世界计算方面还有很长的路要走--比如这个例子;多笔贷款,变化的利率和相关的利息计算等等等等。

© www.soinside.com 2019 - 2024. All rights reserved.