访问 SELECT 语句中的“前一行”值

问题描述 投票:0回答:10

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

我正在寻找这样的东西:

SELECT value - (previous.value) FROM table

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

这有可能以某种方式实现吗?

sql sql-server sql-server-2008
10个回答
151
投票

使用lag功能:

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

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


84
投票

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


37
投票

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


36
投票
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
LEFT JOIN CTE prev on prev.rownum = cur.rownum - 1

15
投票

将表左连接到自身,并计算出连接条件,以便表的连接版本中匹配的行是前一行,用于您对“上一页”的特定定义。

更新:起初我以为你会想要保留所有行,并在没有前一行的情况下使用 NULL。再次阅读它,您只想剔除行,因此您应该使用内部联接而不是左联接。


更新:

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


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

1
投票

在 SQL 查询中引用前一行的另一种方法是使用 递归公用表表达式 (CTE):

CREATE TABLE t (counter INTEGER);

INSERT INTO t VALUES (1),(2),(3),(4),(5);

WITH cte(counter, previous, difference) AS (
  -- Anchor query
  SELECT MIN(counter), 0, MIN(counter)
  FROM t
  UNION ALL
  -- Recursive query
  SELECT t.counter, cte.counter, t.counter - cte.counter
  FROM t JOIN cte ON cte.counter = t.counter - 1
)
SELECT counter, previous, difference
FROM cte
ORDER BY counter;

结果:

柜台 上一页 差异
1 0 1
2 1 1
3 2 1
4 3 1
5 4 1

锚查询生成公共表表达式

cte
的第一行,其中它将
cte.counter
设置为表
t.counter
第一行中的列
t
,将
cte.previous
设置为 0,并将
cte.difference
设置为第一行一排
t.counter

递归查询将公共表表达式

cte
的每一行连接到表
t
的前一行。递归查询中,
cte.counter
指表
t.counter
每一行中的
t
cte.previous
cte.counter
上一行中的
cte
t.counter - cte.counter
指两者的差值列。

请注意,递归 CTE 比 LAG 和 LEAD 函数更灵活,因为一行可以引用前一行的任意结果。 (递归函数或过程是指过程的输入是该过程的前一次迭代的输出,除了第一个输入是常量。)

我在 SQLite Online 测试了这个查询。


1
投票

LAG()
LEAD()
是位置函数。
LAG()
指前一行的数据,
LEAD()
指当前行之后的数据。

这是代码:

SELECT value - LAG(value) OVER(ORDER BY id) FROM table;

0
投票

您可以使用以下函数来获取当前行值和上一行值:

SELECT value,
min(value) over (order by id rows between 1 preceding and 1 
preceding) as value_prev
FROM table

然后您只需从该选择中选择

value - value_prev
即可获得答案

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