要求是编写一个带有2个参数的表值函数。
@start_number (a integer value)
@end_number (a integer value)
该函数将返回一个包含@start_number
和@end_number
之间数字的表格,包括参数编号。
以下是代码:
Declare @start_number int
Declare @end_number int
Declare @max_recursion int
Set @start_number = 10
Set @end_number = 100
Set @max_recursion = (@end_number - @start_number)
Declare @numbers table(number int) --start
;with numbers(number) as
(
select @start_number as number
union all
select number + 1 from numbers where number between @start_number and @end_number - 1
)
insert into @numbers(number)
select number from numbers option(maxrecursion 10000)
select number from @numbers --end
查询提供了所需的输出。但是,我需要从start
到end
的解释,这些行是如何工作的?max recursion
在这里的目的是什么?
答案 0 :(得分:1)
-- table variable declaration
Declare @numbers table(number int) --start
-- CTE declaration
;with numbers(number) as
( -- CTE body
-- anchor
select @start_number as number -- [a]
union all
-- recursive call to itself
select number + 1 from numbers -- [r]
-- recursion limit
where number between @start_number and @end_number - 1
)
insert into @numbers(number)
-- insert into @numbers table all values from CTE
select number from numbers option(maxrecursion 10000)
-- pretty obvious select
select number from @numbers --end
表变量几乎与通常的表一样操作。 关于临时表和表变量之间的区别,请参考this question或msdn。
CTE就像嵌套查询,临时结果集。主要区别在于它可以自我引用(就像你的情况一样)。您只使用一列number
声明CTE。如果可以解析列,则不需要手动指定列。这是一个recursive CTE - 选择一个数字并加入自己的数字附加+1。因此,每个后续行都有+1
到前一行。当您从CTE中选择时,锚select @start_number as number
被执行。比它与所有东西连接,返回形式本身(添加+1)。
让我们一步一步走进CTE:
on [a] return 1
on [r] add +1 to everything from self ([a] and [r]):
on [a] return 1
on [r] add +1 to everything from self ([a] and [r]):
on [a] return 1
on [r] add +1 to everything from self ([a] and [r]):
on [a] return 1
on [r] add +1 to everything from self ([a] and [r]):
...
所以你的结果集是(每个嵌套级别在{
和}
内):
{1, {1, {1, {1, ...} +1 } +1 } +1 }
其中{1} +1 => {2}
,{1, {1} +1 } +1 => {1, 2} +1 => {2, 3}
等等
因此,如果您不限制此查询,嵌套将是无限的。这就是recursion limit
出现的原因。引擎将简单地过滤您的无限查询并停止嵌套,因为在任何后续选择中它将获得与您不匹配的值,并且该选择将返回空结果集。例如,您过滤的值小于10:
....
on [a] return 1 -- will result 8, match
on [r] add +1 to everything from self ([a] and [r]):
on [a] return 1 -- will result in 9, match
on [r] add +1 to everything from self ([a] and [r]): -- (STOP)
on [a] return 1 -- will result in 10, not match
-- nothing else will be queried as on (STOP) empty result returned
-- so, recursion stopped
请注意,如果您在递归调用中过滤不正确,例如
where number between @start_number + 1 and @end_number - 1
第一次递归调用将返回给你@start_number
,这与你的过滤器不匹配,并且不会发生递归。
实际上,过滤
where number between @start_number and @end_number - 1
过度。它可以简化为
where number < @end_number
注意,在这两种情况下,过滤器都不会与@end_number
匹配,因为它会将+1
添加到之前的值。因此,当您从@start_number
(锚点)开始并与递归连接时,将返回
{ @start_number, ..., @end_number -1 } +1
结果将是
{ @start_number, @start_number +1, ..., @end_number -1 +1 }
-- ^^^^^^^^^^^^^ - anchor
-- recursion - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
select语句Hint中的 option(maxrecursion 10000)
会覆盖默认的最大递归级别(默认情况下为100,有效值为[0..32767])。因此,您的特定功能限制为10000个值,如果您尝试生成超过10000个值,您将收到此错误:
声明终止。语句完成之前,最大递归10000已用尽。
请注意,限制递归的过滤器必须放在CTE中,而不是放在CTE的位置,因为在这种情况下引擎会继续迭代CTE寻找下一个匹配值,但是你的CTE实际上是无限的,会发生错误
其余代码非常简单 - 结果表格CTE插入@numbers
表变量然后简单选择。
变量@max_recursion
可以在未使用时删除(并且不能在OPTION
子句中使用)。