我需要计算表格中两行之间的列的差异。有什么办法可以直接在 SQL 中执行此操作吗?我正在使用 Microsoft SQL Server 2008。
我正在寻找这样的东西:
SELECT value - (previous.value) FROM table
想象“前一个”变量引用最新选定的行。当然,通过这样的选择,我最终会在一个有 n 行的表中选择 n-1 行,这不是一个可能,实际上正是我需要的。
这有可能以某种方式实现吗?
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 添加任意数量的列。
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)
上建立索引将大大改善此查询。
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
将表左连接到自身,并计算出连接条件,以便表的连接版本中匹配的行是前一行,用于您对“上一页”的特定定义。
更新:起初我以为你会想要保留所有行,并在没有前一行的情况下使用 NULL。再次阅读它,您只想剔除行,因此您应该使用内部联接而不是左联接。
更新:
较新版本的 Sql Server 还具有可用于此目的的 LAG 和 LEAD 窗口函数。
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
仅当序列中没有间隙时,所选答案才有效。但是,如果您使用自动生成的 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
在 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 测试了这个查询。
LAG()
和 LEAD()
是位置函数。 LAG()
指前一行的数据,LEAD()
指当前行之后的数据。
这是代码:
SELECT value - LAG(value) OVER(ORDER BY id) FROM table;
您可以使用以下函数来获取当前行值和上一行值:
SELECT value,
min(value) over (order by id rows between 1 preceding and 1
preceding) as value_prev
FROM table
然后您只需从该选择中选择
value - value_prev
即可获得答案