Мне нужно свернуть несколько диапазонов последовательных чисел (1 или более) до наборов их минимальных и максимальных значений.У меня есть уникальные целые числа (без дубликатов), хранящиеся в столбце таблицы.
Очевидным способом (для меня) решения этой проблемы является использование cursor
(см. Мой алгоритм ниже) и итерация по каждому целому числу.Тем не менее, это кажется мне неэффективным, поэтому мне интересно, есть ли более эффективный алгоритм.Возможно, есть способ использования общих табличных выражений с рекурсией.У меня более 32767 целых чисел, поэтому любое решение должно использовать option (MAXRECURSION 0)
, который устанавливает неограниченную рекурсию.
Ниже приведен упрощенный тестовый пример для моего существующего алгоритма с именем cursor
.Он выведет минимум и максимум для каждого диапазона последовательных чисел (например, 1-3, 9-11, 13-13, 15-16).
Я использую MS SQL Server 2008. Обратите внимание, комментарии начинаютсяс двумя черточками (-).
declare @minInt int, @maxInt int
declare @nextInt int, @prevInt int
--need a temporary table to store the ranges that were found
declare @rangeTable table (minInt int, maxInt int)
declare mycursor cursor for
select * from
(
select 1 as id union
select 2 as id union
select 3 as id union
select 9 as id union
select 10 as id union
select 11 as id union
select 13 as id union
select 15 as id union
select 16 as id
) tblRanges
order by id--order is needed for this algorithm if used with generic data
open mycursor
--initialise new sequence
fetch next from mycursor into @minInt
select @maxInt = @minInt--set the min and max to the smallest value
select @prevInt = @minInt--store the last int
declare @sequenceFound int
while @@FETCH_STATUS=0
begin
select @sequenceFound=1--set the default flag value to true
--loop while sequence found
while @@FETCH_STATUS=0 and @sequenceFound = 1
begin
fetch next from mycursor into @nextInt
if @nextInt = (@prevInt + 1)
begin
select @sequenceFound = 1
end
else
begin
select @sequenceFound = 0
end
select @prevInt = @nextInt--store the current value as the previous value for the next comparison
if @sequenceFound = 1 --if the nextInt is part of a sequence, then store the new maxInt
and @maxInt < @nextInt--should always be true for ordered output containing no duplicates
begin
select @maxInt = @nextInt
end
end--while sequenceFound
--store the sequence range and then check for more sequences
insert into @rangeTable (minInt,maxInt) values (@minInt,@maxInt)
--store the current value as the new minInt and maxInt for the next sequence iteration
select @minInt = @nextInt
select @maxInt = @nextInt
end--while more table rows found
select * from @rangeTable
close mycursor
deallocate mycursor