有没有办法访问SELECT语句中的“上一行”值?

时间:2009-04-02 15:28:13

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

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

我正在寻找类似的东西:

SELECT value - (previous.value) FROM table

想象“previous”变量引用最新选定的行。当然,对于像这样的选择,我最终将在具有n行的表中选择n-1行,这不是可能的,实际上正是我所需要的。

这有可能吗?

7 个答案:

答案 0 :(得分:52)

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

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

如果您知道如何订购但不知道如何获得当前的值(EG,您希望按字母顺序排序),那么我不知道在标准SQL中如何做到这一点,但大多数SQL实现将有扩展来实现它。

如果您可以对行进行排序,使每个行都不同,则可以使用SQL Server的方法:

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添加任意数量的列。

答案 1 :(得分:46)

使用lag功能:

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

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

答案 2 :(得分:27)

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

答案 3 :(得分:26)

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)上建立索引将大大改善此查询。

答案 4 :(得分:8)

LEFT JOIN将表连接到自身,连接条件计算出来,因此在表的连接版本中匹配的行是前一行,对于您的特定“previous”定义。

更新:起初我以为你想要保留所有行,对于没有前一行的条件,使用NULL。再次阅读它只是想要剔除行,所以你应该是一个内连接而不是左连接。


<强>更新

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

答案 5 :(得分:2)

所选答案仅在序列中没有间隙时才有效。但是,如果您使用自动生成的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

答案 6 :(得分:2)

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