具有相同ID的多行之间的SQL差异

Posted

技术标签:

【中文标题】具有相同ID的多行之间的SQL差异【英文标题】:SQL difference between Multiple Rows having the same ID 【发布时间】:2018-10-14 04:12:53 【问题描述】:

SQL 服务器 2012

原始数据

ID   VAL   Time
+---+----+---------------------+
| 2 |  1 | 2015-05-09 12:54:39 |
| 3 | 10 | 2015-05-09 12:54:39 |
| 2 |  1 | 2015-05-09 12:56:39 |
| 3 | 10 | 2015-05-09 12:56:39 |
| 2 |  5 | 2015-05-09 13:48:30 |
| 3 | 16 | 2015-05-09 13:48:30 |
| 2 |  7 | 2015-05-09 15:01:09 |
| 3 | 20 | 2015-05-09 15:01:09 |
+---+----+---------------------+ 

我有一张表,其中 VAL 会随着时间的推移而永远增加。我想操纵数据以显示随着时间的推移每个 ID 的 VAL 增加了多少。所以时间 2 的值 - 时间 1 的值

理想结果:

ID   VALI   Time
+---+----+---------------------+
| 2 |  0 | 2015-05-09 12:56:39 |
| 3 |  0 | 2015-05-09 12:56:39 |
| 2 |  4 | 2015-05-09 13:48:30 |
| 3 |  6 | 2015-05-09 13:48:30 |
| 2 |  2 | 2015-05-09 15:01:09 |
| 3 |  4 | 2015-05-09 15:01:09 |
+---+----+---------------------+ 

到目前为止的代码:

select
t1.Time,t1.[ID],t2.[VAL]-t1.[VAL] AS [ValI]
from #tempTable t1
inner join #tempTable t2 ON t1.[ID]=t2.[ID]
AND t1.[Time]<t2.[Time]

我需要计算当前时间戳与仅当前时间戳之前的时间之间的差异,而不是当前时间戳之前的所有时间戳。到目前为止,当 VAL 没有改变时,我得到了很多重复值。

【问题讨论】:

查看 Sql Server 的 LAG() 函数。 什么版本的 SQL Server? sql query for difference between current row and previous row based on datetime的可能重复 【参考方案1】:

你可以用这个。

DECLARE @MyTable TABLE (ID INT,   VAL INT,  [Time] DATETIME)
INSERT INTO @MyTable VALUES
(2,  1 ,'2015-05-09 12:54:39'),
(3, 10 ,'2015-05-09 12:54:39'),
(2,  1 ,'2015-05-09 12:56:39'),
(3, 10 ,'2015-05-09 12:56:39'),
(2,  5 ,'2015-05-09 13:48:30'),
(3, 16 ,'2015-05-09 13:48:30'),
(2,  7 ,'2015-05-09 15:01:09'),
(3, 20 ,'2015-05-09 15:01:09')


;WITH CTE AS (
    SELECT *, ROW_NUMBER() OVER(PARTITION BY ID ORDER BY [Time]) RN FROM @MyTable
)
SELECT T1.ID, T2.VAL - T1.VAL AS VALI, T2.Time FROM CTE T1
    INNER JOIN CTE T2 ON T1.ID = T2.ID AND T1.RN = T2.RN - 1
ORDER BY T1.[Time], T1.ID

结果:

ID          VALI        Time
----------- ----------- -----------------------
2           0           2015-05-09 12:56:39.000
3           0           2015-05-09 12:56:39.000
2           4           2015-05-09 13:48:30.000
3           6           2015-05-09 13:48:30.000
2           2           2015-05-09 15:01:09.000
3           4           2015-05-09 15:01:09.000

【讨论】:

看起来可行。有没有办法将 CTE 插入临时表中我需要做一些额外的过滤? 你可以在选择前写insert into。喜欢WITH CTE AS ( ... ) INSERT INTO TempTable SELECT ... @user3055889 什么额外的过滤?插入临时表有点违背 CTE 的主要好处之一?您应该能够根据需要过滤您的 CTE,而无需将其转储到临时表中。 @Shawn 感谢这个想法,刚刚向 CTE 添加了 where 语句,并且能够在那里完成工作。 另外,仅供参考:这绝对是在 SQL LEAD/LAG) 会产生一个平面执行计划,该计划将执行好一点。【参考方案2】:

这应该可以工作:

 select id, time, val-prevval val1 from (
 select * , lag(val, 1, 0) over(partition by  id order by val, time) prevVal from #Temp)A
 order by time

【讨论】:

【参考方案3】:

您可以首先在您的#tempTable 上放置一个排名,按时间降序排列,并按 ID 分区。

那么你的加入变成这样:

select
    t1.Time,
    t1.[ID],
    t1.[VAL] - t2.[VAL] AS [ValI]
from #tempTable t1
inner join #tempTable t2 ON t1.[ID] = t2.[ID]
    AND t2.Rank = (t1.Rank + 1)

【讨论】:

【参考方案4】:

LAG() 在 SQL 2012 中可用。这允许您获取当前行的 val 并从前一行中减去 val,按 id 分组并按 Time 排序。这将返回前两行的NULL,因为它们没有以前的记录可供比较。您可以通过将查询放在子选择中然后应用WHERE valDiff IS NULL 来排除它们,或者您可以使用LAG() > LAG(Val,1,0) 的第三个参数默认valDiff 将前两行默认为0 .

SQL Fiddle

MS SQL Server 2017 架构设置

CREATE TABLE t1 ( ID int, VAL int, [Time] datetime) ;

INSERT INTO t1 ( ID, Val, [Time] )
VALUES 
    ( 2, 1 , '2015-05-09 12:54:39')
  , ( 3, 10, '2015-05-09 12:54:39')
  , ( 2, 1 , '2015-05-09 12:56:39')
  , ( 3, 10, '2015-05-09 12:56:39')
  , ( 2, 5 , '2015-05-09 13:48:30')
  , ( 3, 16, '2015-05-09 13:48:30')
  , ( 2, 7 , '2015-05-09 15:01:09')
  , ( 3, 20, '2015-05-09 15:01:09')
;

查询 1

SELECT s1.ID
  , s1.ValDiff
  , FORMAT(s1.[Time], 'yyyy-MM-dd hh:mm:ss') AS fTime
FROM (
  SELECT ID
    , Val - LAG(Val,1) OVER ( PARTITION BY ID ORDER BY [Time],ID ) AS ValDiff
    , [Time]
  FROM t1
) s1
WHERE s1.valDiff IS NOT NULL
ORDER BY s1.[Time],s1.ID

Results

| ID |    ValI |               fTime |
|----|---------|---------------------|
|  2 |       0 | 2015-05-09 12:56:39 |
|  3 |       0 | 2015-05-09 12:56:39 |
|  2 |       4 | 2015-05-09 01:48:30 |
|  3 |       6 | 2015-05-09 01:48:30 |
|  2 |       2 | 2015-05-09 03:01:09 |
|  3 |       4 | 2015-05-09 03:01:09 |

【讨论】:

【参考方案5】:

如果你有LAG

DEMO

   SELECT
        id
      , val - LAG(val, 1) OVER (PARTITION BY id ORDER BY time ASC) AS VALI
      , time
    FROM #TempTable
    ORDER BY time ASC, ID ASC

【讨论】:

试过了,但我的所有差异都得到了 0 以及一些空值。 @user3055889 适用于我的示例数据。看这个演示rextester.com/FGKB7753

以上是关于具有相同ID的多行之间的SQL差异的主要内容,如果未能解决你的问题,请参考以下文章

SQL:查找具有非唯一特征 ID 的两个表之间的差异?

SQL Server 中是不是有一种方法可以显示两个具有相同布局并共享一些公共数据的表之间的字段差异

找出具有相同形状的两个DataFrame之间的差异

需要找到2个结构相同的sql表之间的差异

多行到一行sql

SQL 查询以查找具有相同列值的多行