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

Posted

技术标签:

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

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

我正在寻找这样的东西:

SELECT value - (previous.value) FROM table

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

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

【问题讨论】:

好吧,只是添加一个对新观众有用的评论。 SQL 2012 现在有 LAG 和 LEAD :) 参考这个链接blog.sqlauthority.com/2013/09/22/… 【参考方案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 扩展进行了改进。 回应“没关系,顺序不是问题”...那你为什么不在查询中减去一个仲裁值,因为如果你不这样做,那你就是在做考虑顺序?

以上是关于有没有办法访问 SELECT 语句中的“上一行”值?的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法在 SQL Select 语句中使用来自 JSON 对象的值?

access不允许在select into 语句中使用多值字段

有没有办法更新一个表,并在一个语句中选择它?

Oracle:select into 查询没有记录的解决办法

在 SQL Select 语句中使用 If else

有没有办法选择引用游标的内容作为 SQL Select 语句的一部分?