从年份列表中获取年份范围列表?

时间:2015-03-27 15:09:59

标签: sql sql-server

是否可以从年份列表中获取年份范围列表?

假设有一个类似

的表格
Year
1990
1991
1992
1999
2001
2002
2015

或者喜欢

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

我不知道从哪里开始;如何在这些年内获得范围? e.g。

1990-1992,1999,2001-2002,2015

2 个答案:

答案 0 :(得分:9)

以下是示例:

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,'');

主要想法是找到所谓的岛屿,在这种情况下,通过在此选择中使用ROW_NUMBER可以轻松制作:

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

将从行号中减去年份,这将标记相同的“岛屿”。这意味着,如果每隔一年按行数增加一个,我们将获得相同的结果编号:

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

此结果编号稍后可用于分组并获取MAX和MIN值。

答案 1 :(得分:2)

获得实际范围的技术称为islands and gaps。要获得汇总在一行中的值,您可以使用不同的技术,但这里的简单数据在变量中累积数据将会正常工作。

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