Maximum stored procedure, function, trigger, or view nesting level exceeded (limit 32) in function checking SwissQR references

huangapple go评论56阅读模式
英文:

Maximum stored procedure, function, trigger, or view nesting level exceeded (limit 32) in function checking SwissQR references

问题

我创建了一个用于检查瑞士QR支付参考的SQL函数。这个支付参考需要包含一个由26 + 1位数字组成的“QR参考”字段,其中最后一位是使用递归函数获得的验证位。

你可以使用不同的参考进行测试,比如(000000810877053913500061524, 903751000001009522863019583, 000000000000000002023031440, 000000000000000000002185921等)。

但当你尝试在选择进项发票的草稿时执行上述查询时,出现了以下错误:

存储过程、函数、触发器或视图的最大嵌套级别超过了限制(限制为32)。

你不理解的部分是,函数只能调用自身26次,而不是32次。

你尝试在最后的查询中添加了"top 1",但SQL Server仍然回应嵌套级别超过了32的限制。

当你将, [dbo].[fnCheckQRReference](ii.QRReference, 0) as 'Ref_OK'这一行注释掉后,你得到了46个结果,其中只有一个出现了错误(Ref_OK = 0)。

有人能看出我的错误在哪里吗?

答:问题可能出在函数的递归调用上。虽然函数理论上最多只会调用自身26次,但SQL Server 限制函数嵌套调用的总次数为32。这意味着不仅包括函数自身的调用,还包括函数作为查询中的子查询时的调用。

在你的查询中,你在选择草稿发票时调用了函数 [dbo].[fnCheckQRReference](ii.QRReference, 0),这可能导致了嵌套调用超过32次的问题,因为 SQL Server 将函数调用视为嵌套。

为了解决这个问题,你可以尝试减少函数递归的深度,或者考虑使用循环或其他方法来检查QR参考而不是递归函数。这样可以避免函数嵌套调用超过限制。

英文:

We have a new iso for payment. These payment needs to contain a 'QR reference' field made of 26 + 1 digits. The last one is a validation digit obtained using a recursive function.

I created a SQL function allowing us to know wether a 'QR reference' is valid or not:

create  function [dbo].[fnCheckQRReference](@Reference nvarchar(28), @Range int = 0)
returns bit as
/* Check if the SwissQR payment reference is correct
 * Return 1 if QR Reference is correct
 * Return 0 if not
 */
begin
	if not(@Reference not like '%[^0-9]%')
		return 0
	if len(@Reference) > 27
		return 0

	declare   @tmp as	int = cast(substring(@Reference, 1, 1) as int)
		, @result	as	int;

	with tab as (
		select 0 as [id], 0 as [val] union select 1, 9 union select 2, 4 union select 3, 6 union select 4, 8 union select 5, 2 union select 6, 7 union select 7, 1 union select 8, 3 union select 9, 5
	)
	select	@result = val
	from	tab
	where	id	= (@Range + @tmp) % 10

	if len(@Reference) = 2
		if	(((10 - @result) % 10) = cast(substring(@Reference, 2, 26) as int))
			return 1
		else
			return 0

	return	[dbo].[fnCheckQRReference](substring (@Reference, 2, 26), @result);
end
go

You can test it with different references (000000810877053913500061524, 903751000001009522863019583, 000000000000000002023031440, 000000000000000000002185921, etc.)

My problem reside later when I want to select drafts of incoming invoices

select	  cast(ii.InvoiceNumber as nvarchar(15))		as	'Invoice number'
		, ii.QRReference								as	'QR_Reference'
		, [dbo].[fnCheckQRReference](ii.QRReference, 0)	as	'Ref_OK'
--
from	Incoming_Invoices		as	ii	with(nolock)
--
where   ii.InvoiceStatus not in ('D', 'E')	-- Looking only for drafts and error statuses

When executing the previous query, I get the error:
> Maximum stored procedure, function, trigger, or view nesting level exceeded (limit 32).

What I do not understand here is that the function can only call itself 26 times, never 32.

I tried adding a top 1 in my last query but still, SQL Server answered that the nesting level exceeded the limit of 32.

When I commented the , [dbo].[fnCheckQRReference](ii.QRReference, 0) as 'Ref_OK' line, I had 46 result out of which only one is in error. (Ref*_*OK = 0)

Does someone see where is my mistake(s)?

答案1

得分: 1

你需要从递归改为迭代。递归的层次达到28层太多了,不够可靠,因为调用代码只有很少的帧。

在一个循环中,

set @Reference = substring(@Reference, 2, 26)

直到你消耗完整个字符串。

英文:

You'll need to switch from recursion to iteration. 28 levels of recursion is too much to be reliable, because you only have a few frames for the calling code.

In a loop

set @Reference = substring(@Reference, 2, 26)

until you've consumed the whole string.

答案2

得分: 1

下面是您要翻译的代码部分:

create VIEW [custom].[QR_code_modulo_digit]
AS
select 0 as [id], 0 as [val] union select 1, 9 union select 2, 4 union select 3, 6 union select 4, 8 union select 5, 2 union select 6, 7 union select 7, 1 union select 8, 3 union select 9, 5
alter function [dbo].[fnCheckQRReference](@Reference nvarchar(28))
returns bit as
/* 检查 Esker 发送的 QR 付款参考是否正确
 * 如果 QR 参考正确,返回 1
 * 如果不正确,返回 0
 */
begin
	if not(@Reference not like '%[^0-9]%')
		return 0
	if len(@Reference) != 27
		return 0

	declare   @tmp		as	int = cast(substring(@Reference, 1, 1) as int)
			, @result	as	int = 0;
	
	while (len(@Reference) > 1)
	begin
		select	@result		= val	from	[custom].[QR_code_modulo_digit]	where	id	= (@tmp + @result) % 10
		set		@Reference	=		substring(@Reference, 2, 26);
		set		@tmp		= cast(	substring(@Reference, 1, 1) as int);
	end

	if	(((10 - @result) % 10) = cast(@Reference as int))
		return 1
	
	return 0
end
go

希望对您有所帮助!

英文:

So I did as you told me. First, I created a view containing the required data to calculate the last digit:

create VIEW [custom].[QR_code_modulo_digit]
AS
select 0 as [id], 0 as [val] union select 1, 9 union select 2, 4 union select 3, 6 union select 4, 8 union select 5, 2 union select 6, 7 union select 7, 1 union select 8, 3 union select 9, 5

Then my function with a 'while' loop :

alter function [dbo].[fnCheckQRReference](@Reference nvarchar(28))
returns bit as
/* Check if the reference sent by Esker for QR payment is correct
 * Return 1 if QR Reference is correct
 * Return 0 if not
 */
begin
	if not(@Reference not like '%[^0-9]%')
		return 0
	if len(@Reference) != 27
		return 0

	declare   @tmp		as	int = cast(substring(@Reference, 1, 1) as int)
			, @result	as	int = 0;
	
	while (len(@Reference) > 1)
	begin
		select	@result		= val	from	[custom].[QR_code_modulo_digit]	where	id	= (@tmp + @result) % 10
		set		@Reference	=		substring(@Reference, 2, 26);
		set		@tmp		= cast(	substring(@Reference, 1, 1) as int);
	end

	if	(((10 - @result) % 10) = cast(@Reference as int))
		return 1
	
	return 0
end
go

It is a little less elegant than my previous recursiv function but works fine and it has some good sides such as the fact that the function has ony one parameter and we can test it only once.

Thank you Davide Browne from Microsoft!

huangapple
  • 本文由 发表于 2023年3月9日 20:57:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/75684954.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定