sql-server - 从 MSSQL 中的表中获取数字范围

标签 sql-server sql-server-2008 sql-server-2008-r2 user-defined-functions

我在 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 进行分组,并使用 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;

其中,对于 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/

相关文章:

c# - 我应该将 .pdf 文档保存到数据库还是需要搜索的文件夹中

sql - 使用 exec() 在 sp 内执行运行时查询时遇到错误 "The default schema does not exist."

sql-server-2008-r2 - SQL Server Management Studio-消失的服务器名称

java - 如何使 Java 与 SQL Server 一起工作?

sql - RAISERROR WITH NOWAIT 缓冲的解决方法

sql - 在 T-SQL 中创建复杂的数据透视表

.net - SQLCLR 使用错误版本的 .NET Framework

数据库卡在恢复状态

mysql - 删除数据库中没有引用的条目

sql - 如何查找具有表 A 主键外键约束的表列表?