从MSSQL中的表中获取Numbers范围

Ana*_*hah 3 sql-server user-defined-functions sql-server-2008 sql-server-2008-r2

我在MSSQL 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

我想要的是当我搜索ID = 1时,我想要结果如此

1-6,16,31

当我搜索ID = 2时,我想要结果

55-57,81-84

当我搜索ID = 3时,我想要结果

101-103,107-109

您可以使用以下脚本来创建表和数据:

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)
Run Code Online (Sandbox Code Playgroud)

谢谢

Gar*_*thD 7

这是一个空白和岛屿问题,关键是确定你的连续范围,这是使用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
Run Code Online (Sandbox Code Playgroud)

从引脚地址中扣除行号将为每个连续范围提供一个常量值:

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
Run Code Online (Sandbox Code Playgroud)

到目前为止的查询很简单:

SELECT  ID,
        PinAddress,
        GroupingSet = PinAddress - ROW_NUMBER() OVER(PARTITION BY ID ORDER BY PinAddress)
FROM    dbo.PinAddress;
Run Code Online (Sandbox Code Playgroud)

然后,您可以按常量值和ID进行分组,并使用MINMAX获取每个范围的开始和结束:

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;
Run Code Online (Sandbox Code Playgroud)

其中,对于ID 3给出:

ID  RangeStart  RangeEnd    RangeText
-----------------------------------------
3   101         103         101 - 103
3   107         109         107 - 109
Run Code Online (Sandbox Code Playgroud)

最后,您需要将RangeText值连接成一行,这可以使用SQL Server的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;
Run Code Online (Sandbox Code Playgroud)

这使:

ID      Ranges
------------------------------
1       1 - 6, 16 - 16, 31 - 31
2       55 - 57, 81 - 84
3       101 - 103, 107 - 109
Run Code Online (Sandbox Code Playgroud)