将序列号转换为 SQL 中缺失值的单列
Convert sequential numbers into single column with missing values in SQL
我有一个站点的示例 ID 列表,格式如下:
站点名称、样本编号,这样给定站点有 n 个样本编号。例如,数据可以是:
site1 | 1
site1 | 2
etc 到任意 n.
使用以下类似示例,下面的数据将从最后一个 select 语句中得到答案:
CREATE TABLE #SiteWithId(SiteId VARCHAR(50), SampleNumber INT)
INSERT INTO #SiteWithId
(
SiteId,
SampleNumber
)
values
( 'test', -- SiteId - varchar(50)
1 -- SampleNumber - int
),
('test',2),
('test',3),
('test',4),
('test',6),
('test',7)
SELECT * FROM #SiteWithId
DROP TABLE #SiteWithId
--the answer
SELECT 'test', '1-4,6-7'
请注意,缺少的项目会导致最终答案中断。
我知道我可以在 C# 中循环遍历数据集并创建这样的项目。但是有没有人知道只使用 sql 来创建这样的值,这样我就可以吐出报告所需的值?我想我也可以在 sql 中做一个循环,但我担心它无法扩展,因为这并不是 sql 真正要做的。
除了 sql 或 c# 中的循环之外,还有更好的方法吗?
这是一个依赖于 window 函数的解决方案。记录的 SampleNumber
与具有相同 SiteName
的记录组中的 ROW_NUMBER()
之间的差异为您提供了它所属的组。然后,外部查询聚合每个组:
SELECT SiteName, CONCAT(MIN(SampleNumber), '-', MAX(SampleNumber)) SampleRange
FROM (
SELECT
SiteName,
SampleNumber,
ROW_NUMBER() OVER(PARTITION BY SiteName ORDER BY SampleNumber) rn
FROM mytable
) x
GROUP BY SiteName, (SampleNumber - rn)
示例数据:
SiteName | SampleNumber
:------- | -----------:
site1 | 1
site1 | 2
site1 | 3
site1 | 5
site1 | 6
site1 | 8
site1 | 9
site1 | 10
结果:
SiteName | SampleRange
:------- | :----------
site1 | 1-3
site1 | 5-6
site1 | 8-10
如果您希望每个站点的所有范围连接在一个记录中,您可以添加另一级别的聚合并使用 STRING_AGG()
(自 SQL Server 2017 起可用):
SELECT SiteName, STRING_AGG(SampleRange,',') SampleRange
FROM (
SELECT SiteName, CONCAT(MIN(SampleNumber), '-', MAX(SampleNumber)) SampleRange
FROM (
SELECT
SiteName,
SampleNumber,
ROW_NUMBER() OVER(PARTITION BY SiteName ORDER BY SampleNumber) rn
FROM mytable
) x
GROUP BY SiteName, (SampleNumber - rn)
) y
GROUP BY SiteName
Demo:
SiteName | SampleRange
:------- | :-----------
site1 | 1-3,5-6,8-10
我有一个站点的示例 ID 列表,格式如下: 站点名称、样本编号,这样给定站点有 n 个样本编号。例如,数据可以是:
site1 | 1
site1 | 2
etc 到任意 n.
使用以下类似示例,下面的数据将从最后一个 select 语句中得到答案:
CREATE TABLE #SiteWithId(SiteId VARCHAR(50), SampleNumber INT)
INSERT INTO #SiteWithId
(
SiteId,
SampleNumber
)
values
( 'test', -- SiteId - varchar(50)
1 -- SampleNumber - int
),
('test',2),
('test',3),
('test',4),
('test',6),
('test',7)
SELECT * FROM #SiteWithId
DROP TABLE #SiteWithId
--the answer
SELECT 'test', '1-4,6-7'
请注意,缺少的项目会导致最终答案中断。
我知道我可以在 C# 中循环遍历数据集并创建这样的项目。但是有没有人知道只使用 sql 来创建这样的值,这样我就可以吐出报告所需的值?我想我也可以在 sql 中做一个循环,但我担心它无法扩展,因为这并不是 sql 真正要做的。
除了 sql 或 c# 中的循环之外,还有更好的方法吗?
这是一个依赖于 window 函数的解决方案。记录的 SampleNumber
与具有相同 SiteName
的记录组中的 ROW_NUMBER()
之间的差异为您提供了它所属的组。然后,外部查询聚合每个组:
SELECT SiteName, CONCAT(MIN(SampleNumber), '-', MAX(SampleNumber)) SampleRange
FROM (
SELECT
SiteName,
SampleNumber,
ROW_NUMBER() OVER(PARTITION BY SiteName ORDER BY SampleNumber) rn
FROM mytable
) x
GROUP BY SiteName, (SampleNumber - rn)
示例数据:
SiteName | SampleNumber :------- | -----------: site1 | 1 site1 | 2 site1 | 3 site1 | 5 site1 | 6 site1 | 8 site1 | 9 site1 | 10
结果:
SiteName | SampleRange :------- | :---------- site1 | 1-3 site1 | 5-6 site1 | 8-10
如果您希望每个站点的所有范围连接在一个记录中,您可以添加另一级别的聚合并使用 STRING_AGG()
(自 SQL Server 2017 起可用):
SELECT SiteName, STRING_AGG(SampleRange,',') SampleRange
FROM (
SELECT SiteName, CONCAT(MIN(SampleNumber), '-', MAX(SampleNumber)) SampleRange
FROM (
SELECT
SiteName,
SampleNumber,
ROW_NUMBER() OVER(PARTITION BY SiteName ORDER BY SampleNumber) rn
FROM mytable
) x
GROUP BY SiteName, (SampleNumber - rn)
) y
GROUP BY SiteName
Demo:
SiteName | SampleRange :------- | :----------- site1 | 1-3,5-6,8-10