【问题标题】:Get Numbers Range from Table in MSSQL从 MSSQL 中的表中获取数字范围
【发布时间】:2015-08-28 08:58:18
【问题描述】:

我在 MSSQL 2008R2 中有一个表:

 标识 |引脚地址
-------------------------------------
   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)

谢谢

【问题讨论】:

  • 1-6,16,31?这些分区背后的逻辑是什么?我想不通
  • 你能解释一下选择数据的逻辑吗?你用的是什么标准???
  • @ArunprasanthKV 没有收到您的问题。
  • 这可能是我的问题,但我没有明白你的逻辑,我的意思是 1-6,16,31 ,55-57,81-84,101-103,107-109 是怎么来的

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


【解决方案1】:

基本版,如果要获取所有 ID 的结果集,请创建一个标量函数。

declare @id int = 1
declare @formed varchar(max)
Select @Formed = ISNULL(@formed+',','')+Formed
from
(
Select  
 Formed = convert(varchar,MIN([PinAddress]))
            +case when MIN([PinAddress]) != MAX([PinAddress]) then '-'
            +convert(varchar,MAX([PinAddress] )) else '' end
from PinAddress  where ID = @id            
group by [PinAddress]/case when [#PinAddress]/10= 0 then 10 else 5 end)t

select @formed

【讨论】:

  • Azar 您的查询几乎可以解决问题。但是当我检查 ID = 3 时,它不会产生预期的结果。当我检查 ID = 3 时,它会生成输出 101-109,它应该是 101-103,107-109。你能查一下吗?
  • 对不起,我没注意到你现在可以试试 SQL。
  • 如果您的第一个序列从 1 到 11,您的代码将不起作用
【解决方案2】:

试试这个代码

DECLARE @values VARCHAR(8000) 
DECLARE @prevseq int
SET @values = ''



SELECT @values = @values + 
    (CASE WHEN @values = '' OR @values like '%,' THEN cast(PinAddress as varchar) --first value or new after sequence
          WHEN PinAddress - 1 = @prevseq THEN '' 
          ELSE '-' + cast (@prevseq as varchar) + ',' + cast(PinAddress as varchar) 
     END),
        @prevseq = coalesce(PinAddress, -1)
FROM PinAddress 
WHERE ID = 1
ORDER BY PinAddress ASC

SELECT @values = @values + 
        (CASE WHEN @values not like '%' + cast(@prevseq as varchar) THEN '-' + cast(@prevseq as varchar) ELSE '' END)

PRINT @values

【讨论】:

    【解决方案3】:

    这是一个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
    

    【讨论】:

      【解决方案4】:

      @GarethD 你对 ROW_NUMBER 的逻辑非常好,而且效果很好。 我接受了您的查询的帮助并对其进行了一些更改以获得我想要的输出:

      WITH RankedData 作为 ( 选择身份证, 引脚地址, GroupingSet = PinAddress - ROW_NUMBER() OVER(PARTITION BY ID ORDER BY PinAddress) FROM dbo.PinAddress 其中 ID = 1 ) 选择 p.ID, 范围=东西( ( 当 MIN(pinaddress) = MAX(PINADDRESS) THEN 时选择 CASE ', ' + CONVERT(VARCHAR(10), MIN(PinAddress)) 别的 ', ' + CONVERT(VARCHAR(10), MIN(PinAddress)) + '-' + 转换(VARCHAR(10),MAX(引脚地址)) 结尾 FROM RankedData AS rd 其中 rd.ID = p.ID 通过...分组 ID, 分组集 对于 XML 路径(''), 类型 ).value('.', 'VARCHAR(MAX)'), 1、 2、 '' ) 从 ( 选择不同的 ID 来自排名数据 ) 作为 p;

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2022-11-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多