识别SQL Server中的序列开始和结束
我有一个表,其中的数据按如下方式排列:识别SQL Server中的序列开始和结束,sql,sql-server,Sql,Sql Server,我有一个表,其中的数据按如下方式排列: ID | BOUNDARY | TIMESTAMP 1 | NULL | 2016-01-01 00:20:00 2 | A | 2016-01-01 00:20:10 3 | A | 2016-01-01 00:20:14 4 | A | 2016-01-01 00:20:22 5 | NULL | 2016-01-01 00:20:38 6 | A
ID | BOUNDARY | TIMESTAMP
1 | NULL | 2016-01-01 00:20:00
2 | A | 2016-01-01 00:20:10
3 | A | 2016-01-01 00:20:14
4 | A | 2016-01-01 00:20:22
5 | NULL | 2016-01-01 00:20:38
6 | A | 2016-01-01 00:20:45
7 | B | 2016-01-01 00:21:02
8 | B | 2016-01-01 00:21:12
9 | A | 2016-01-01 00:21:16
10 | A | 2016-01-01 00:21:22
11 | C | 2016-01-01 00:21:30
12 | A | 2016-01-01 00:21:35
13 | A | 2016-01-01 00:21:40
14 | A | 2016-01-01 00:21:46
15 | A | 2016-01-01 00:21:50
我想做的是找到一种有效的方法来标记SQL Server 2014中序列开始和结束的ID和时间戳。当一个边界不为空并且至少连续重复两次时,一个段将被删除。例如,第一段来自IDs 2-4,第二段来自IDs 7-8,第三段来自IDs 9-10
我首先尝试的方法是创建两列,一个startflag col和一个endflag列。我创建了一个正确标记开始和结束的更新查询,但我想创建一个视图,在其中我可以将其视为一条记录,如下所示:
BOUNDARY | START ID | END ID
A | 2 | 4
B | 7 | 8
A | 9 | 10
A | 12 | 15
好的,我相信有更好的方法可以做到这一点,但这是可行的:
WITH CTE AS
(
SELECT *,
RN1 = ROW_NUMBER() OVER(ORDER BY [TIMESTAMP]),
RN2 = ROW_NUMBER() OVER(PARTITION BY BOUNDARY ORDER BY [TIMESTAMP])
FROM #YourTable
), CTE2 AS
(
SELECT *,
RN1-RN2 RN3,
COUNT(*) OVER(PARTITION BY RN1-RN2) N
FROM CTE
)
SELECT BOUNDARY,
MIN(ID) [START ID],
MAX(ID) [END ID]
FROM CTE2
WHERE N > 1
AND BOUNDARY IS NOT NULL
GROUP BY BOUNDARY, RN3
ORDER BY [START ID];
如果我们使用此示例表:
CREATE TABLE #YourTable
([ID] int, [BOUNDARY] varchar(4), [TIMESTAMP] datetime)
;
INSERT INTO #YourTable
([ID], [BOUNDARY], [TIMESTAMP])
VALUES
(1, NULL, '2016-01-01 00:20:00'),
(2, 'A', '2016-01-01 00:20:10'),
(3, 'A', '2016-01-01 00:20:14'),
(4, 'A', '2016-01-01 00:20:22'),
(5, NULL, '2016-01-01 00:20:38'),
(6, 'A', '2016-01-01 00:20:45'),
(7, 'B', '2016-01-01 00:21:02'),
(8, 'B', '2016-01-01 00:21:12'),
(9, 'A', '2016-01-01 00:21:16'),
(10, 'A', '2016-01-01 00:21:22'),
(11, 'C', '2016-01-01 00:21:30'),
(12, 'A', '2016-01-01 00:21:35'),
(13, 'A', '2016-01-01 00:21:40'),
(14, 'A', '2016-01-01 00:21:46'),
(15, 'A', '2016-01-01 00:21:50')
;
结果是:
╔══════════╦══════════╦════════╗
║ BOUNDARY ║ START ID ║ END ID ║
╠══════════╬══════════╬════════╣
║ A ║ 2 ║ 4 ║
║ B ║ 7 ║ 8 ║
║ A ║ 9 ║ 10 ║
║ A ║ 12 ║ 15 ║
╚══════════╩══════════╩════════╝
关键是通过以下方式创建岛屿分组: 根据您的ID时间计算行号 计算每个不同值的行数 分组=1-2 请看下面的示例:
declare @T table (ID int, BOUNDARY char(1), [TIMESTAMP] datetime2)
insert into @T values (1, null, '2016-01-01 00:20:00'), (2, 'A', '2016-01-01 00:20:10'), (3, 'A', '2016-01-01 00:20:14'), (4, 'A', '2016-01-01 00:20:22'), (5, null, '2016-01-01 00:20:38'), (6, 'A', '2016-01-01 00:20:45'), (7, 'B', '2016-01-01 00:21:02'), (8, 'B', '2016-01-01 00:21:12'), (9, 'A', '2016-01-01 00:21:16'), (10, 'A', '2016-01-01 00:21:22'), (11, 'C', '2016-01-01 00:21:30'), (12, 'A', '2016-01-01 00:21:35'), (13, 'A', '2016-01-01 00:21:40'), (14, 'A', '2016-01-01 00:21:46'), (15, 'A', '2016-01-01 00:21:50')
select
BOUNDARY,
min(ID) as [START ID],
max(id) as [END ID]
from
(
select
ID,
BOUNDARY,
ID -
row_number() over (partition by BOUNDARY order by TIMESTAMP) as grp
from @T as t
) as T
where BOUNDARY is not null
group by grp, BOUNDARY
having count(*) >= 2
order by min(ID)
为什么最终答案中不包括边界=C?因为没有两条记录?因为一行中至少需要有两个实例。+1但我认为按时间戳排序应该是ID,特别是当时间戳可能出现问题时,例如,将记录3更改为1天后,您会得到一个令人不快的结果。@Aducci,谢谢,但不完全是这样。我在上面尝试过,它将所有“A”边界分组为一个记录,开始为2,结束为15。实际上,我需要在上面的3个单独的“A”记录。但是,这是一个开始,我可能可以使用它。@user3150002-脚本为我输出正确的结果。不确定您运行的是什么?+1,但我认为按时间戳排序应该是ID,特别是当时间戳可能出现问题时,例如,将记录3更改为1天后,您会得到一个令人不快的结果。@Lamak:我对数据的一小部分进行了测试,它似乎有效!我的表有数千行数据,所以让我运行整个集合并验证。@user3150002没有问题。慢慢来,根据需要进行测试