Как расширить строки из count только в tsql - PullRequest
3 голосов
/ 21 января 2009

У меня есть таблица, которая содержит число и значение диапазона. Например, один столбец имеет значение 40, а другой столбец имеет значение 100, означающее, что начиная с 40 диапазон имеет 100 значений, заканчивающихся на 139 включительно числа 40. Я хочу написать оператор tsql, который расширяет мои данные в отдельные строк.

Я думаю, что мне нужен cte для этого, но я не знаю, как мне этого добиться.

Примечание: в развернутом виде я ожидаю 7м строк.

Ответы [ 4 ]

3 голосов
/ 21 января 2009

Если вы хотите CTE, вот пример:

Начальная вставка:

insert into rangeTable (StartValue, RangeValue)
select 40,100
union all select 150,10
go

запрос:

with r_CTE (startVal, rangeVal, generatedVal)
as
(
    select r.startValue, r.rangeValue, r.startValue
    from rangeTable r
    union all
    select r.startValue, r.rangeValue, generatedVal+1
    from rangeTable r
    inner join r_CTE rc 
        on r.startValue = rc.startVal
        and r.rangeValue = rc.rangeVal
        and r.startValue +  r.rangeValue > rc.generatedVal + 1
)
select * from r_CTE
order by startVal, rangeVal, generatedVal

Просто имейте в виду, что максимальное количество рекурсий по умолчанию равно 100. Вы можете изменить его до максимального значения 32767, вызвав

option (maxrecursion 32767)

или без ограничений

option (maxrecursion 0)

Подробнее см. BOL

1 голос
/ 21 января 2009

Я не знаю, как это можно сделать с помощью общих табличных выражений, но вот решение с использованием временной таблицы:

   SET NOCOUNT ON

   DECLARE @MaxValue INT
   SELECT @MaxValue = max(StartValue + RangeValue) FROM MyTable

   DECLARE @Numbers table ( 
      Number INT IDENTITY(1,1) PRIMARY KEY 
   )

   INSERT @Numbers DEFAULT VALUES 

   WHILE COALESCE(SCOPE_IDENTITY(), 0) <= @MaxValue 
      INSERT @Numbers DEFAULT VALUES 

    SELECT n.Number
    FROM   @Numbers n
    WHERE  EXISTS(
        SELECT *
        FROM   MyTable t
        WHERE  n.Number BETWEEN t.StartValue AND t.StartValue + t.RangeValue - 1
    )

    SET NOCOUNT OFF

Может быть оптимизировано, если таблица Numbers была обычной таблицей. Поэтому вам не нужно заполнять временную таблицу при каждом вызове.

0 голосов
/ 21 января 2009

Я бы сделал что-то немного отличное от splattne ...

SET NOCOUNT ON

DECLARE @MaxValue INT
DECLARE @Numbers table (
    Number INT IDENTITY(1,1) PRIMARY KEY CLUSTERED
 )

SELECT @MaxValue = max(RangeValue) FROM MyTable
INSERT @Numbers DEFAULT VALUES

WHILE COALESCE(SCOPE_IDENTITY(), 0) <= @MaxValue
    INSERT @Numbers DEFAULT VALUES

SELECT
    t.startValue + n.Number
FROM
    MyTable t
INNER JOIN
    @Numbers n
        ON n.Number < t.RangeValue

SET NOCOUNT OFF

Это сведет к минимуму количество строк, которое нужно вставить в переменную таблицы, а затем использовать объединение, чтобы «умножить» одну таблицу на другую ...

По характеру запроса таблица исходной таблицы не нуждается в индексации, но таблица «числа» должна иметь индекс (или первичный ключ). Кластерные индексы относятся к тому, как они хранятся на диске, поэтому я не могу видеть, что CLUSTERED здесь уместен, но я оставил его, как только что скопировал из Splattne.

(Большие объединения могут быть медленными, но все же намного быстрее, чем миллионы вставок.)

0 голосов
/ 21 января 2009

Вы можете попробовать этот подход:

create function [dbo].[fRange](@a int, @b int)
    returns @ret table (val int)
as 
begin
    declare @val int
    declare @end int
    set @val = @a
    set @end = @a + @b
    while @val < @end
    begin
        insert into @ret(val)
        select @val
        set @val = @val+1
    end
return
end

go

declare @ranges table(start int, noOfEntries int)

insert into @ranges (start, noOfEntries)
select 40,100
union all select 150, 10

select * from @ranges r
    cross apply dbo.fRange(start,noOfEntries ) fr

не самый быстрый, но должен работать

...