Как выбрать несколько диапазонов, разделенных идентификатором группы

#sql #sql-server #tsql #sql-server-2016

#sql #sql-сервер #tsql #sql-server-2016

Вопрос:

Мне нужно создать разделенный запятыми список значений для каждого идентификатора группы в таблице и заполнить все пропущенные значения, но я не совсем уверен, как поступить. Я использую Server 2016.

моя таблица:

   id  |  group_id  |  value  
------ ------------ ---------
   1  |         1  |    15
   2  |         1  |    16
   3  |         1  |    20
   4  |         2  |    12
   5  |         2  |    15
   6  |         3  |     8
  

ожидаемый результат:

  group_id  |         list
----------- -----------------------
        1  |  '15,16,17,18,19,20'
        2  |  '12,13,14,15'
        3  |  '8'
  

Ответ №1:

Вот альтернативный способ. Создайте функцию, которая принимает начальные и конечные номера и генерирует недостающие номера.

 create function dbo.get_list(@start int, @end int)
returns varchar(1000)
as begin
    declare @retval varchar(1000);
    set @retval = cast(@start as varchar(1000));
    set @start = @start   1;

    while @start <= @end
    begin
        set @retval = @retval   ','   cast(@start as varchar(1000));
        set @start = @start   1;
    end;

    return @retval
end
  

Как только это будет сделано, напишите этот SQL:

 select group_id, dbo.get_list(min(value), max(value))
from test
group by group_id
  

Результат

 group_id  list
--------  ------------------
1         15,16,17,18,19,20
2         12,13,14,15
3         8
  

Ответ №2:

Я думаю, что рекурсивный CTE — хороший подход:

 with t as (
      select v.*
      from (values (1, 1, 15),
                   (2, 1, 16),
                   (3, 1, 20),
                   (4, 2, 12),
                   (5, 2, 15),
                   (6, 3, 8)
           ) v(  id, group_id, value )
     ),
     cte as (
      select t.group_id, min(t.value) as value, max(t.value) as maxvalue, convert(varchar(max), min(t.value)) as vals, 1 as lev
      from t
      group by t.group_id
      union all
      select cte.group_id, value   1, maxvalue, concat(vals, ',', value   1), lev   2
      from cte
      where value < maxvalue
     )
select cte.group_id, cte.vals
from (select cte.*, max(cte.lev) over (partition by cte.group_id) as maxlev
      from cte
     ) cte
where lev = maxlev
order by group_id;
  

Здесь есть db<>fiddle .