我有一个包含数字和范围值的表。例如,一列的值为 40,另一列的值为 100,这意味着从 40 开始,范围有 100 个以 139 结尾的值,包括数字 40。我想编写一个 tsql 语句,将我的数据扩展为单个行。
我想我需要一个 cte,但不知道如何实现这一点。
注意:扩展时我期待 7m 行。
我有一个包含数字和范围值的表。例如,一列的值为 40,另一列的值为 100,这意味着从 40 开始,范围有 100 个以 139 结尾的值,包括数字 40。我想编写一个 tsql 语句,将我的数据扩展为单个行。
我想我需要一个 cte,但不知道如何实现这一点。
注意:扩展时我期待 7m 行。
如果你想要 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 _
我不知道如何使用公用表表达式来完成,但这里有一个使用临时表的解决方案:
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 表是常规表,则可以优化。因此,您不必在每次调用时填写临时表。
你可以试试这种方法:
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
不是最快的,但应该可以
我会做一些与 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 复制的地方。
(像这样的大连接可能很慢,但仍然比数百万次插入快得多。)