我在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)
由于
答案 0 :(得分:7)
这是一个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
答案 1 :(得分:1)
试试此代码
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
答案 2 :(得分:0)
@GarethD你的逻辑与ROW_NUMBER非常好,并且像一个魅力。 我接受了你的查询的帮助并稍微改了一下以得到我想要的输出:
WITH RankedData AS ( SELECT ID, PinAddress, GroupingSet = PinAddress - ROW_NUMBER() OVER(PARTITION BY ID ORDER BY PinAddress) FROM dbo.PinAddress WHERE ID = 1 ) SELECT p.ID, Ranges = STUFF( ( SELECT CASE WHEN MIN(pinaddress) = MAX(PINADDRESS) THEN ', ' + CONVERT(VARCHAR(10), MIN(PinAddress)) ELSE ', ' + CONVERT(VARCHAR(10), MIN(PinAddress)) + '-' + 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 RankedData ) AS p;
答案 3 :(得分:-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