В SQL можно ли сделать последовательный выбор диапазона более эффективно, чем мой алгоритм (см. Код), в котором используется курсор? - PullRequest
2 голосов
/ 05 августа 2010

Мне нужно свернуть несколько диапазонов последовательных чисел (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

1 Ответ

2 голосов
/ 05 августа 2010

Предоставлено Ициком Бен-Ганом:

WITH tblRanges AS
( 
    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 
),
StartingPoints AS
(
SELECT ID, ROW_NUMBER() OVER(ORDER BY ID) AS rownum
FROM tblRanges AS A
WHERE NOT EXISTS
(SELECT *
FROM tblRanges AS B
WHERE B.ID = A.ID - 1)
),
EndingPoints AS
(
SELECT ID, ROW_NUMBER() OVER(ORDER BY ID) AS rownum
FROM tblRanges AS A
WHERE NOT EXISTS
(SELECT *
FROM tblRanges AS B
WHERE B.ID = A.ID + 1)
)
SELECT S.ID AS start_range, E.ID AS end_range
FROM StartingPoints AS S
JOIN EndingPoints AS E
ON E.rownum = S.rownum;

Вы можете прочитать полное объяснение из его главы в SQL Sever MVP Deep Dives под названием Пробелы и острова .Он объясняет различные методы (включая курсоры) и сравнивает их с точки зрения производительности.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...