【问题标题】:Calculating value using previous value of a row in T-SQL使用 T-SQL 中行的先前值计算值
【发布时间】:2011-02-23 16:23:46
【问题描述】:

我得到了下表,想在不使用游标或 while 循环的情况下使用 sql 中前一行的同一列 (Column2) 的值来计算每一行的 Column2 的值。

Id   Date             Column1    Column2
1    01/01/2011       5          5 => Same as Column1
2    02/01/2011       2          18 => (1 + (value of Column2 from the previous row)) * (1 + (Value of Column1 from the current row)) i.e. (1+5)*(1+2)
3    03/01/2011       3          76 => (1+18)*(1+3) = 19*4
and so on

有什么想法吗?


【问题讨论】:

    标签: sql-server tsql


    【解决方案1】:

    假设recursive CTE 至少为 SQL Server 2005:

    ;with cteCalculation as (
        select t.Id, t.Date, t.Column1, t.Column1 as Column2
            from YourTable t
            where t.Id = 1
        union all
        select t.Id, t.Date, t.Column1, (1+t.Column1)*(1+c.Column2) as Column2
            from YourTable t
                inner join cteCalculation c
                    on t.Id-1 = c.id
    )
    select c.Id, c.Date, c.Column1, c.Column2
        from cteCalculation c
    

    【讨论】:

    • 只是好奇。我试图自己实现一个递归 CTE,但想知道如果你不能假设 Id 是连续的,该怎么做?
    • @mellamokb 我认为你可以有一个 with cluase 来执行 row_num() over (order by ...),然后在递归 CTE 中引用它,但我不确定,不要这里没有 SQL。您也可以查看lead() 和lag()。实际上,如果您使用 Lead 和 Lag,则不需要 CTE,它们也会更快。
    • @gjvdkamp:row_number() 的好主意!不幸的是,SQL Server 没有 LEAD 和 LAG。
    • 您好,很抱歉领先和滞后,在 oracle 上花了太多时间,SQL 上似乎不存在。
    • 我发布了我一直在研究的示例,并实现了您的 ROW_NUMBER() 想法。
    【解决方案2】:

    我解决了刚才提到的问题。

    这是我的代码:

    ;with cteCalculation as (
        select t.Id, t.Column1, t.Column1 as Column2
            from table_1 t
            where t.Id = 1
        union all
        select t.Id, t.Column1, (1+t.Column1)*(1+c.Column2) as Column2
            from table_1 t
                inner join cteCalculation c
                    on t.Id-1 = c.id
    ),
    cte2 as(
    select t.Id, t.Column1 as Column3
            from table_1 t
            where t.Id = 1
        union all
        select t.Id, (select column2+1 from cteCalculation c where c.id = t.id)  as Column3
            from table_1 t
                inner join cte2 c2
                    on t.Id-1 = c2.id
    )
    
    select c.Id, c.Column1, c.Column2, c2.column3
        from cteCalculation c
    inner join cte2 c2 on c.id = c2.id
    

    结果如我所料:

    1           5   5   5
    2           2   18  19
    3           3   76  77
    

    【讨论】:

    • 如果您是提出此问题的人,请标记此答案以引起版主注意,以便合并您的帐户。
    【解决方案3】:

    这是一个使用 ROW_NUMBER() 的示例,如果 Id 不一定按顺序排列:

    ;with DataRaw as (
        select 1 as Id, '01/01/11' as Date, 5 as Column1 union
        select 2 as Id, '02/01/11' as Date, 2 as Column1 union
        select 4 as Id, '03/01/11' as Date, 3 as Column1
    ),
    Data as (
        select RowId = ROW_NUMBER() over (order by Id), Id, Date, Column1 from DataRaw
    ),
    Data2 as (
        select
            RowId, id, Date, Column1, Column1 as Column2
        from
            Data d
        where
            RowId = 1
    
        union all
    
        select
            d1.RowId, d1.id, d1.Date, d1.Column1, (1+d1.column1)*(1+d2.column2) as column2
        from
            Data d1
        cross join
            Data2 d2
        where
            d2.RowId + 1 = d1.RowId
    )
    select
        Id, Date, Column1, Column2
    from
        Data2
    

    【讨论】:

      【解决方案4】:

      编辑:应该更好地阅读这个问题......

      另一场比赛是这样的:

      ;with DataRaw as (
          select 1 as Id, '01/01/11' as Date, 5 as Column1 union
          select 2 as Id, '02/01/11' as Date, 2 as Column1 union
          select 4 as Id, '03/01/11' as Date, 3 as Column1
      ),
      Data as (
          select Ord = ROW_NUMBER() over (order by Id), Id, Date, Column1 from DataRaw
      ),
      select -- formula goes here, using current and prev as datasources.
      from  data current
            left join data prev on current.Ord = prev.Ord + 1  -- pick the previous row by adding 1 to the ordinal
      

      我认为到达前一行的普通连接会比 CTE 更快。不过,您必须自己检查。

      对我来说看起来更容易。

      祝你好运,GJ

      【讨论】:

      • @gjdkamp:这只会根据两个连续的行设置值。计算需要在整个数据集上累积,AFAIK 只能通过递归 CTE 或类似游标的东西来完成,而 OP 不想使用。
      • 进一步详细说明:您将需要对 d2.Column2 的引用,以便可以使用先前记录的值,该值将不存在。查询需要看起来像这样:select d1.Ord, d1.Id, d1.Date, d1.Column1, (1+d1.column1)*(1+d2.column2) as column2 from data d1 left join data d2 on d1.Ord = d2.Ord + 1,但是 d2.Column2 来自哪里,因为基础表没有 Column2 源列?
      • 好吧抱歉,我没有在原帖的代码视图中向右滚动。我们将表与自身连接起来,然后我们将需要的两行数据(当前和以前)放在一起。然后你应该能够进行计算。我重命名了别名以说明这一点。
      • 没有。您无法从当前行和上一行计算 Column2。您需要将当前行和之前的所有行放在一起。在您的情况下,您也无权访问前一行的 Column2 计算,因为两行的每个组合都与其余行隔离在一起。请随意用在 SQL Server 中测试的工作示例证明我错了。
      • 啊我明白了,不,我也不这么认为,CTE 是要走的路。
      猜你喜欢
      • 2017-04-30
      • 2021-12-21
      • 2021-11-13
      • 1970-01-01
      • 2014-10-18
      • 1970-01-01
      • 1970-01-01
      • 2016-01-10
      • 2013-04-15
      相关资源
      最近更新 更多