sql server如何解压数据
我想对同一组中的数据进行解压 让我举例说明 演示数据:sql server如何解压数据,sql,sql-server,Sql,Sql Server,我想对同一组中的数据进行解压 让我举例说明 演示数据: +-------------+----+------------+-----------+ | Primarykey | ID | START_Point| STOP_Point| +-------------+----+------------+-----------+ | 1 | 1 | 1 | 10 | | 2 | 1 | 2 |
+-------------+----+------------+-----------+
| Primarykey | ID | START_Point| STOP_Point|
+-------------+----+------------+-----------+
| 1 | 1 | 1 | 10 |
| 2 | 1 | 2 | 4 |
| 3 | 1 | 5 | 5 |
| 4 | 1 | 5 | 5 |
| 5 | 1 | 5 | 15 |
| 6 | 2 | 5 | 5 |
| 7 | 2 | 2 | 2 |
| 8 | 2 | 1 | 10 |
| 9 | 2 | 1 | 20 |
+-------------+----+------------+-----------+
数据应与组中的数据解套:
预期产出:
+------------+-----------+------------+-----------+
| primarykey | SubjectID | START_Point| STOP_Point|
+------------+-----------+------------+-----------+
| 1 | 1 | 1 | 1 |
| 1 | 1 | 2 | 4 |
| 1 | 1 | 5 | 5 |
| 1 | 1 | 6 | 10 |
| 2 | 1 | 2 | 4 |
| 3 | 1 | 5 | 5 |
| 4 | 1 | 5 | 5 |
| 5 | 1 | 5 | 5 |
| 5 | 1 | 6 | 10 |
| 5 | 1 | 11 | 15 |
| 6 | 2 | 5 | 5 |
| 7 | 2 | 2 | 2 |
| 8 | 2 | 1 | 1 |
| 8 | 2 | 2 | 2 |
| 8 | 2 | 3 | 4 |
| 8 | 2 | 5 | 5 |
| 8 | 2 | 6 | 10 |
| 9 | 2 | 1 | 1 |
| 9 | 2 | 2 | 2 |
| 9 | 2 | 3 | 4 |
| 9 | 2 | 5 | 5 |
| 9 | 2 | 6 | 10 |
| 9 | 2 | 11 | 20 |
+------------+-----------+------------+-----------+
逻辑:
同一组中不应有任何重叠的起始点-停止点范围
说明:
按“ID”分组
对于主键=1
我们有起点=1和终点=10
现在,如果我们检查其他行的ID=1(因为groupbyid),那么我们可以看到
主键2、3、4和5哪个数据在1-10范围内重叠
所以我们想从1-10到1-1,2-4,5-5,6-10去崩塌 每行应用相同的逻辑 我不想使用游标或逐行获取和处理逻辑,因为我的查询将应用于批量数据,这将降低处理速度 请让我知道,如果我没有解释任何事情或任何事情看起来困惑你 这有点挑战性,但我知道我们有很多关于堆栈溢出的专家 寻找一些固溶体
提前感谢。您可以使用以下查询作为起点:
SELECT ID, Idx, StartStop,
LEAD(Idx) OVER (PARTITION BY ID ORDER BY IdxRowNum) AS NextIdx,
LEAD(StartStop) OVER (PARTITION BY ID ORDER BY IdxRowNum) AS NextStartStop
FROM (
SELECT ID, Idx, StartStop, ROW_NUMBER() OVER (PARTITION BY ID ORDER BY Idx, StartStop) AS IdxRowNum
FROM
(SELECT ID, START_Point, STOP_Point
FROM @T) Src
UNPIVOT (
Idx FOR StartStop IN (START_Point, STOP_Point)
) AS Unpvt
) t
为原始表中包含的每个ID的每个时间跨度创建一个起点-终点表
使用上述查询作为基础,您可以获得每个ID的所有现有间隔:
SELECT DISTINCT ID, StartIdx, StopIdx
FROM
(
SELECT ID,
CASE
WHEN StartStop = 'START_Point' AND NextStartStop = 'START_Point' THEN
CASE WHEN NextIdx > Idx THEN Idx ELSE NULL END
WHEN StartStop = 'STOP_Point' AND NextStartStop = 'STOP_Point' THEN
CASE WHEN NextIdx > Idx THEN Idx + 1 ELSE NULL END
WHEN StartStop = 'STOP_Point' AND NextStartStop = 'START_Point' THEN
CASE WHEN NextIdx > Idx THEN Idx + 1 ELSE NULL END
WHEN StartStop = 'START_Point' AND NextStartStop = 'STOP_Point' THEN Idx
END AS StartIdx,
CASE
WHEN StartStop = 'START_Point' AND NextStartStop = 'START_Point' THEN
CASE WHEN NextIdx > Idx THEN NextIdx - 1 ELSE NULL END
WHEN StartStop = 'STOP_Point' AND NextStartStop = 'STOP_Point' THEN
CASE WHEN NextIdx > Idx THEN NextIdx ELSE NULL END
WHEN StartStop = 'START_Point' AND NextStartStop = 'STOP_Point' THEN NextIdx
WHEN StartStop = 'STOP_Point' AND NextStartStop = 'START_Point' THEN
CASE WHEN NextIdx = Idx + 1 THEN NextIdx WHEN NextIdx > Idx THEN NextIdx- 1 ELSE NULL END
ELSE Idx
END AS StopIdx
FROM
(
SELECT ID, Idx, StartStop,
LEAD(Idx) OVER (PARTITION BY ID ORDER BY IdxRowNum) AS NextIdx,
LEAD(StartStop) OVER (PARTITION BY ID ORDER BY IdxRowNum) AS NextStartStop
FROM (
SELECT ID, Idx, StartStop, ROW_NUMBER() OVER (PARTITION BY ID ORDER BY Idx, StartStop) AS IdxRowNum
FROM
(SELECT ID, START_Point, STOP_Point
FROM @T) Src
UNPIVOT (
Idx FOR StartStop IN (START_Point, STOP_Point)
) AS Unpvt
) t
) s
) u
WHERE StartIdx IS NOT NULL AND StopIdx IS NOT NULL
上述查询的输出为:
ID StartIdx StopIdx
-----------------------
1 1 1
1 2 4
1 5 5
1 6 10
1 11 15
2 1 1
2 2 2
2 3 4
2 5 5
2 6 10
2 11 20
在CTE中使用前面的查询并执行交叉应用
最终得到您想要的:
; WITH IntervalsByID AS
(
SELECT DISTINCT ID, StartIdx, StopIdx
FROM
(
SELECT ID,
CASE
WHEN StartStop = 'START_Point' AND NextStartStop = 'START_Point' THEN
CASE WHEN NextIdx > Idx THEN Idx ELSE NULL END
WHEN StartStop = 'STOP_Point' AND NextStartStop = 'STOP_Point' THEN
CASE WHEN NextIdx > Idx THEN Idx + 1 ELSE NULL END
WHEN StartStop = 'STOP_Point' AND NextStartStop = 'START_Point' THEN
CASE WHEN NextIdx > Idx THEN Idx + 1 ELSE NULL END
WHEN StartStop = 'START_Point' AND NextStartStop = 'STOP_Point' THEN Idx
END AS StartIdx,
CASE
WHEN StartStop = 'START_Point' AND NextStartStop = 'START_Point' THEN
CASE WHEN NextIdx > Idx THEN NextIdx - 1 ELSE NULL END
WHEN StartStop = 'STOP_Point' AND NextStartStop = 'STOP_Point' THEN
CASE WHEN NextIdx > Idx THEN NextIdx ELSE NULL END
WHEN StartStop = 'START_Point' AND NextStartStop = 'STOP_Point' THEN NextIdx
WHEN StartStop = 'STOP_Point' AND NextStartStop = 'START_Point' THEN
CASE WHEN NextIdx = Idx + 1 THEN NextIdx WHEN NextIdx > Idx THEN NextIdx- 1 ELSE NULL END
ELSE Idx
END AS StopIdx
FROM
(
SELECT ID, Idx, StartStop,
LEAD(Idx) OVER (PARTITION BY ID ORDER BY IdxRowNum) AS NextIdx,
LEAD(StartStop) OVER (PARTITION BY ID ORDER BY IdxRowNum) AS NextStartStop
FROM (
SELECT ID, Idx, StartStop, ROW_NUMBER() OVER (PARTITION BY ID ORDER BY Idx, StartStop) AS IdxRowNum
FROM
(SELECT ID, START_Point, STOP_Point
FROM @T) Src
UNPIVOT (
Idx FOR StartStop IN (START_Point, STOP_Point)
) AS Unpvt
) t
) s
) u
WHERE StartIdx IS NOT NULL AND StopIdx IS NOT NULL
)
SELECT t.PrimaryKey, t.ID, s.StartIdx, s.StopIdx
FROM @T AS t
CROSS APPLY
(
SELECT *
FROM IntervalsByID
WHERE ID = t.ID AND t.START_Point <= StartIdx AND t.STOP_Point >= StopIdx
) s
ORDER BY PrimaryKey, StartIdx
因此,我们要将1-10解折叠为1-1,2-4,5-5,6-10。这是如何完成的?
primarykey=5
将起点和终点显示为5-15。它是如何变成6-10
?对于id=2
,它将如何工作?我们可以帮助您找到解决方案。但首先你需要提供问题的细节。您试图以某种方式扩展数据,但我们不了解业务规则是什么。从你发布的内容来看,这对我来说毫无意义。“decollapse”不是一个词,我想你的意思是扩展。每一行都有一个唯一的键吗?@user2315555,5-15应该扩展到5-5,6-10和11-15,因为已经有一个范围5-5和1-10,所以我们需要将5-15拆分为5-5(我们有一行5-5),6-10(我们有一行1-10)和11-15。。。你的解决方案很有魅力。非常感谢。。你真是个明星。。这个问题看起来很简单,但它的解决方案同样复杂。你运用了很好的逻辑…请让我知道我是否可以在任何网站或博客上关注你。。再次非常感谢。。。
PrimaryKey ID StartIdx StopIdx
--------------------------------------
1 1 1 1
1 1 2 4
1 1 5 5
1 1 6 10
2 1 2 4
3 1 5 5
4 1 5 5
5 1 5 5
5 1 6 10
5 1 11 15
6 2 5 5
7 2 2 2
8 2 1 1
8 2 2 2
8 2 3 4
8 2 5 5
8 2 6 10
9 2 1 1
9 2 2 2
9 2 3 4
9 2 5 5
9 2 6 10
9 2 11 20