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


93

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

我正在寻找这样的东西:

SELECT value - (previous.value) FROM table

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

有可能吗?


6
好吧,只是添加一条对新观众有用的评论。SQL 2012现在具有LAG和LEAD了:)请参阅此链接blog.sqlauthority.com/2013/09/22/…–
KD

Answers:


61

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

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

如果您知道如何对事物进行排序,但不知道如何在给定当前值的情况下获得先前的值(例如,您要按字母顺序进行排序),那么我不知道在标准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添加尽可能多的列。


没关系,顺序不是问题,我只是从示例中删除了它,以使其更简单,我将尝试一下。
Edwin Jarvis,2009年

7
假设主键是按顺序生成的,并且行不会被删除,并且select没有任何其他order子句和and and ...
MartinStettner

马丁是正确的。尽管这在某些情况下可能有效,但您实际上确实需要从业务角度准确定义“上一个”的含义,最好不依赖于生成的ID。
汤姆H”

没错,我使用SQL Server扩展添加了一项改进。
RossFabricant

2
响应“没关系,顺序不是问题” ...那么为什么不在查询中减去一个任意值,因为如果您不考虑顺序,这就是您要执行的操作?
JohnFx

79

使用滞后功能:

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

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


1
这是PostgreSQL解决方案。问题是关于MSSQL。MSSQL具有版本这种功能2012+(msdn.microsoft.com/en-us/en-en/library/hh231256(v=sql.120).aspx
Kromster

10
@KromStern不仅是PostgreSQL解决方案。SQL Window功能是在SQL:2003标准中引入的。
汉斯·金泽尔

LAG函数可以采用三个参数:LAG(ExpressionToSelect, NumberOfRowsToLag, DefaultValue)。滞后的默认行数是1,但是您可以指定该行,并且在无法滞后时选择默认值,因为您位于集合的开始。
vaindil '18

29

Oracle,PostgreSQL,SQL Server和许多其他RDBMS引擎具有称为的分析功能,LAG并且LEAD可以完成此任务。

在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)将大大改善此查询。


14
SQL Server 2012现在也具有LAG和LEAD。
ErikE

Hana SQL脚本还支持LAG和LEAD。
mik

只是向到达此处寻求在Hive中进行操作的观看者添加另一条评论。它还具有LAG和LEAD功能。文档在这里:cwiki.apache.org/confluence/display/Hive/...
海梅卡法雷尔

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

如果查询中没有分组,它可以正常工作;但是,如果我们只想从一个组中的先前值中减去值,又说相同的EmployeeID,那该怎么办呢?Coz运行此功能仅适用于每个组的前2行,不适用于该组中的其余行。为此,我使用在while循环中运行此代码,但这似乎非常慢。在这种情况下,我们还能采用其他方法吗?那也仅在SQL Server 2008中吗?
Hemant Sisodia

10

将表本身左联接,并计算出联接条件,以便表的联接版本中匹配的行在前一行(对于您的“上一个”的特定定义)。

更新:起初我在想您要保留所有行,对于没有上一行的情况,请使用NULL。再次阅读它,您只希望剔除这些行,因此您应该使用内部联接,而不是左联接。


更新:

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


3
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

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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.