简体   繁体   中英

Grouping by multiple ranges in SQL Server

I tried to search for a solution, but with no success. How can I group my table from looking like this:

from    |    to    |    zone
1       |    1     |      1
1       |    2     |      1
1       |    3     |      1
1       |    4     |      2
1       |    5     |      2
1       |    6     |      2
1       |    7     |      1
1       |    8     |      1
1       |    9     |      1
1       |    10    |      9
2       |    1     |      7
2       |    2     |      7
2       |    3     |      7
2       |    4     |      2
2       |    5     |      2
2       |    6     |      2
2       |    7     |      7
2       |    8     |      7
2       |    9     |      7

To look like this :

from    |    to      |    zone
1       |    1-3     |      1
1       |    4-6     |      2
1       |    7-9     |      1
1       |     10     |      9
2       |    1-3     |      7
2       |    4-6     |      2
2       |    7-9     |      7

Thank you for your help

One approach here is to use the difference of row numbers method, using to to column as one row number, and a row number over a partition using from and zone as the other row number. It is a bit difficult to explain why this works in so many words. It might be best to view the demo link below to explore the query.

WITH cte AS (
    SELECT *,
       ROW_NUMBER() OVER (PARTITION BY [from], zone ORDER BY [to]) rn
    FROM yourTable
)
SELECT
    t.[from],
    CONVERT(varchar(10), MIN(t.[to])) + '-' + CONVERT(varchar(10), MAX([to])) AS [to],
    t.zone
FROM cte t
GROUP BY
    t.[from],
    t.zone,
    t.[to] - t.rn
ORDER BY
    t.[from],
    MIN(t.[to]);

Demo here:

Rextester

This is generally called as Gaps and Islands problem. If you are using SQL Server 2012+ then

;WITH cte
     AS (SELECT *,
                Sum(CASE WHEN zone = prev_zone THEN 0 ELSE 1 END)OVER(partition BY [from] ORDER BY [to]) AS grp
         FROM   (SELECT *,
                        Lag(zone)OVER(partition BY [from] ORDER BY [to]) AS prev_zone
                 FROM   yourtable ) cs ([from], [to], zone)) a)
SELECT [from],
       [to] = Concat(Min([to]), '-', Max([to])),
       zone = Min(zone) 
FROM   cte
GROUP  BY [from],grp
;with mycte
AS
(
select
    ,[from]
    ,min([to]) minto
    ,max([to]) maxto
    ,[zone]
from
    mytable
group by
    [from]
    ,[zone]
)
    [from]                      AS [from]
    ,concat(minto, '-', maxto)  AS [to]
    ,[zone]                     AS [zone]
from
    mycte

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM