简体   繁体   中英

Get list of year ranges from list of years?

Is it possible to get a list of year ranges from a list of years?

Say there is a table like

Year
1990
1991
1992
1999
2001
2002
2015

Or like

Years
1990,1991,1992,1999,2001,2002,2015

I am not sure where to start; how can one get the ranges within those years? eg

1990-1992,1999,2001-2002,2015

Here is example:

SELECT *
INTO #years
FROM (VALUES 
(1990),
(1991),
(1992),
(1999),
(2001),
(2002),
(2015)) AS D(Year)

SELECT STUFF((
SELECT ',' +
    CASE 
        WHEN MIN(Year) = MAX(Year) THEN CAST(MIN(Year) AS VARCHAR(9))
        WHEN MIN(Year) <> MAX(Year) THEN CAST(MIN(Year) AS VARCHAR(4)) + '-' +CAST(MAX(Year) AS VARCHAR(4))
    END AS [text()]
FROM (
    SELECT Year
        ,Year-ROW_NUMBER() OVER (ORDER BY Year) AS rowID
    FROM #years) a
GROUP BY rowID
FOR XML PATH('')
),1,1,'');

The main idea is to find so called islands, which in this case is easy made by using ROW_NUMBER in this select:

SELECT Year ,Year-ROW_NUMBER() OVER (ORDER BY Year) 

Years will be subtracted from row numbers, which will mark same "islands". Meaning, if every next year are increasing by one as row number does, we will get same result number:

YEAR  RowNR RESULT  
1999  1     1998  
2000  2     1998  
2015  3     2012  

This result numbers can be later used for grouping and getting MAX and MIN values.

The technique to get actual ranges is known as islands and gaps . And to get values aggregated in one row you can use different techniques, but here's simple one with accumulating data in the variable will work fine.

declare @temp table (y int)
declare @res nvarchar(max)

insert into @temp (y)
values
    (1990),
    (1991),
    (1992),
    (1999),
    (2001),
    (2002),
    (2015)

;with cte_rn as (
    select
        row_number() over(order by y) as rn, y
    from @temp
), cte_rng as (
    select
        case
            when count(*) = 1 then cast(min(y) as nvarchar(max))
            else cast(min(y) as nvarchar(max)) + '-' + cast(max(y) as nvarchar(max))
        end as rng
    from cte_rn
    group by y - rn
)
select @res = isnull(@res + ', ', '') + rng
from cte_rng

sql fiddle demo

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