【问题标题】:Is there a way to access the "previous row" value in a SELECT statement?有没有办法访问 SELECT 语句中的“上一行”值?
【发布时间】:2010-10-17 03:34:49
【问题描述】:

我需要计算表格两行之间的列的差异。有什么方法可以直接在 SQL 中执行此操作吗?我正在使用 Microsoft SQL Server 2008。

我正在寻找这样的东西:

SELECT value - (previous.value) FROM table

假设“previous”变量引用了最新选择的行。当然,通过这样的选择,我最终会在一个有 n 行的表中选择 n-1 行,这不是可能的,实际上正是我需要的。

这在某种程度上是可能的吗?

【问题讨论】:

标签: sql sql-server sql-server-2008


【解决方案1】:

LEFT JOIN 表自身,连接条件已确定,因此在表的连接版本中匹配的行是前一行,用于您对“上一个”的特定定义。

更新:起初我想你会想要保留所有行,在没有前一行的情况下使用 NULL。再读一遍,你只想剔除那些行,所以你应该使用内连接而不是左连接。


更新:

较新版本的 Sql Server 也具有可用于此目的的 LAG 和 LEAD Windowing 函数。

【讨论】:

    【解决方案2】:

    Oracle、PostgreSQL、SQL Server 和更多的 RDBMS 引擎具有称为 LAGLEAD 的分析函数,可以执行此操作。

    在 2012 之前的 SQL Server 中,您需要执行以下操作:

    SELECT  value - (
            SELECT  TOP 1 value
            FROM    mytable m2
            WHERE   m2.col1 < m1.col1 OR (m2.col1 = m1.col1 AND m2.pk < m1.pk)
            ORDER BY 
                    col1, pk
            )
    FROM mytable m1
    ORDER BY
          col1, pk
    

    ,其中COL1 是您订购的列。

    (COL1, PK) 上建立索引将大大改进此查询。

    【讨论】:

    • SQL Server 2012 现在也有 LAG 和 LEAD。
    • Hana SQL 脚本也支持 LAG 和 LEAD。
    • 只是为了给到达这里并在 Hive 中寻找这样做的观众添加另一条评论。它还具有 LAG 和 LEAD 功能。此处的文档:cwiki.apache.org/confluence/display/Hive/…
    【解决方案3】:

    使用lag函数:

    SELECT value - lag(value) OVER (ORDER BY Id) FROM table
    

    用于 Id 的序列可以跳过值,因此 Id-1 并不总是有效。

    【讨论】:

    • 这是 PostgreSQL 解决方案。问题是关于 MSSQL。 MSSQL 在 2012+ 版本中有这样的功能 (msdn.microsoft.com/en-us/en-en/library/hh231256(v=sql.120).aspx)
    • @KromStern 不仅是 PostgreSQL 解决方案。 SQL Window functions 是在 SQL:2003 标准中引入的。
    • LAG 函数可以采用三个参数:LAG(ExpressionToSelect, NumberOfRowsToLag, DefaultValue)。延迟的默认行数是 1,但您可以指定该值以及当您处于集合开始时不可能延迟时选择的默认值。
    【解决方案4】:
    select t2.col from (
    select col,MAX(ID) id from 
    (
    select ROW_NUMBER() over(PARTITION by col order by col) id ,col from testtab t1) as t1
    group by col) as t2
    

    【讨论】:

      【解决方案5】:
      WITH CTE AS (
        SELECT
          rownum = ROW_NUMBER() OVER (ORDER BY columns_to_order_by),
          value
        FROM table
      )
      SELECT
        curr.value - prev.value
      FROM CTE cur
      INNER JOIN CTE prev on prev.rownum = cur.rownum - 1
      

      【讨论】:

      • 如果查询中没有分组,它可以正常工作,但是如果我们只想从一个组中的前一个值中减去值怎么办,比如说相同的 EmployeeID,那么我们该怎么做呢?因为运行它仅适用于每组的前 2 行,而不适用于该组中的其余行。为此,我使用在 while 循环中运行此代码,但这似乎很慢。在这种情况下我们可以采取任何其他方法吗?这也只在 SQL Server 2008 中?
      【解决方案6】:

      只有在序列中没有间隙的情况下,所选答案才有效。但是,如果您使用的是自动生成的 id,则由于插入回滚,序列中可能会出现间隙。

      如果你有差距,这个方法应该可以工作

      declare @temp (value int, primaryKey int, tempid int identity)
      insert value, primarykey from mytable order by  primarykey
      
      select t1.value - t2.value from @temp  t1
      join @temp  t2 
      on t1.tempid = t2.tempid - 1
      

      【讨论】:

        【解决方案7】:

        SQL 没有内置的顺序概念,因此您需要按某个列进行排序才能有意义。像这样的:

        select t1.value - t2.value from table t1, table t2 
        where t1.primaryKey = t2.primaryKey - 1
        

        如果您知道如何排序,但不知道如何在给定当前值的情况下获取前一个值(EG,您想按字母顺序排序),那么我不知道在标准 SQL 中如何做到这一点,但大多数 SQL实现将有扩展来做到这一点。

        如果您可以对行进行排序以使每个行都是不同的,那么这是一种适用于 SQL 服务器的方法:

        select  rank() OVER (ORDER BY id) as 'Rank', value into temp1 from t
        
        select t1.value - t2.value from temp1 t1, temp1 t2 
        where t1.Rank = t2.Rank - 1
        
        drop table temp1
        

        如果您需要打破平局,您可以根据需要向 ORDER BY 添加任意数量的列。

        【讨论】:

        • 没关系,顺序不是问题,我只是从示例中删除它以使其更简单,我会尝试。
        • 假设,主键是按顺序生成的,行永远不会被删除,并且 select 没有任何其他 order 子句 and and and ...
        • 马丁是正确的。尽管这在某些情况下可能有效,但您确实需要准确定义“以前”在商业意义上的含义,最好不要依赖生成的 ID。
        • 你说得对,我使用 SQL Server 扩展进行了改进。
        • 回应“没关系,顺序不是问题”...那你为什么不在查询中减去一个仲裁值,因为如果你不这样做,那你就是在做考虑顺序?
        猜你喜欢
        • 1970-01-01
        • 2021-12-15
        • 2021-02-04
        • 2016-03-27
        • 2019-08-07
        • 2021-08-22
        • 1970-01-01
        • 2022-01-24
        • 2014-09-03
        相关资源
        最近更新 更多