我在 SQL 2008R2 中有一个表:
ID | PinAddress ------------------------------------- 1 | 1 1 | 2 1 | 3 1 | 4 1 | 5 1 | 6 1 | 16 1 | 31 2 | 55 2 | 56 2 | 57 2 | 81 2 | 82 2 | 83 2 | 84 3 | 101 3 | 102 3 | 103 3 | 107 3 | 108 3 | 109
What I want is when I search for ID = 1,I want result like
1-6,16,31
When I search for ID = 2,I want result like
55-57,81-84
When I search for ID = 3,I want result like
101-103,107-109
You can use below script to create table and data:
CREATE TABLE PinAddress(ID INT,PinAddress INT)
INSERT INTO PinAddress values(1,1)
INSERT INTO PinAddress values(1,2)
INSERT INTO PinAddress values(1,3)
INSERT INTO PinAddress values(1,4)
INSERT INTO PinAddress values(1,5)
INSERT INTO PinAddress values(1,6)
INSERT INTO PinAddress values(1,16)
INSERT INTO PinAddress values(1,31)
INSERT INTO PinAddress values(2,55)
INSERT INTO PinAddress values(2,56)
INSERT INTO PinAddress values(2,57)
INSERT INTO PinAddress values(2,81)
INSERT INTO PinAddress values(2,82)
INSERT INTO PinAddress values(2,83)
INSERT INTO PinAddress values(2,84)
INSERT INTO PinAddress values(3,101)
INSERT INTO PinAddress values(3,102)
INSERT INTO PinAddress values(3,103)
INSERT INTO PinAddress values(3,107)
INSERT INTO PinAddress values(3,108)
INSERT INTO PinAddress values(3,109)
谢谢
最佳答案
这是一个 gaps and islands problem ,关键是识别您的连续范围,这是使用 ROW_NUMBER()
完成的。所以对于 ID 3,你有:
ID PinAddress RowNumber
---------------------------
3 101 1
3 102 2
3 103 3
3 107 4
3 108 5
3 109 6
从引脚地址中减去行号将为每个连续范围提供一个常数值:
ID PinAddress RowNumber (PinAddress - RowNumber)
---------------------------------------------------
3 101 1 100
3 102 2 100
3 103 3 100
---------------------------------------------------
3 107 4 103
3 108 5 103
3 109 6 103
到目前为止的查询很简单:
SELECT ID,
PinAddress,
GroupingSet = PinAddress - ROW_NUMBER() OVER(PARTITION BY ID ORDER BY PinAddress)
FROM dbo.PinAddress;
然后您可以按常量值和 ID 进行分组,并使用 MIN
和 MAX
获取每个范围的开始和结束:
WITH RankedData AS
( SELECT ID,
PinAddress,
GroupingSet = PinAddress - ROW_NUMBER() OVER(PARTITION BY ID ORDER BY PinAddress)
FROM PinAddress
)
SELECT ID,
RangeStart = MIN(PinAddress),
RangeEnd = MAX(PinAddress),
RangeText = CONVERT(VARCHAR(10), MIN(PinAddress)) +
CASE WHEN MIN(PinAddress) = MAX(PinAddress) THEN ''
ELSE ' - ' + CONVERT(VARCHAR(10), MAX(PinAddress))
END
FROM RankedData
GROUP BY ID, GroupingSet;
其中,对于 ID 3 给出:
ID RangeStart RangeEnd RangeText
-----------------------------------------
3 101 103 101 - 103
3 107 109 107 - 109
最后,您需要将 RangeText
值连接成一行,这可以使用 SQL Server's XML Extensions 来完成.
WITH RankedData AS
( SELECT ID,
PinAddress,
GroupingSet = PinAddress - ROW_NUMBER() OVER(PARTITION BY ID ORDER BY PinAddress)
FROM PinAddress
)
SELECT p.ID,
Ranges = STUFF((SELECT ', ' + CONVERT(VARCHAR(10), MIN(PinAddress)) +
CASE WHEN MIN(PinAddress) = MAX(PinAddress) THEN ''
ELSE ' - ' + CONVERT(VARCHAR(10), MAX(PinAddress))
END
FROM RankedData AS rd
WHERE rd.ID = p.ID
GROUP BY ID, GroupingSet
FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)'), 1, 2, '')
FROM (SELECT DISTINCT ID FROM PinAddress) AS p;
给出:
ID Ranges
------------------------------
1 1 - 6, 16 - 16, 31 - 31
2 55 - 57, 81 - 84
3 101 - 103, 107 - 109
关于sql-server - 从 MSSQL 中的表中获取数字范围,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30801547/