我有一个包含业务逻辑的存储过程。在它里面我有大约 1609 个变量(不要问我为什么,这就是引擎的工作原理)。我尝试将SET
一个变量连接到所有其他变量的连接值。结果在创建过程中出现错误:
消息 8631,级别 17,状态 1,过程 XXX,行 YYY 内部错误:已达到服务器堆栈限制。请在您的查询中寻找潜在的深层嵌套,并尝试简化它。
我发现错误是由于我需要在SET
操作中使用的变量数量造成的。我可以通过将其分成两部分来执行任务。
我的问题是这方面有一些限制吗?我查了,但没有找到。
我们检查了此 KB中描述的错误,但这不是我们的情况。我们在代码中不使用任何CASE
表达式。我们使用该临时变量来准备必须使用 CLR 函数替换的值列表。我们将 SQL Server 更新到 SP3 CU6(最新),但我们仍然遇到错误。
由于 SQL Server 解析和绑定此类语句的方式(作为两个输入串联的嵌套列表),长
SET
或变量赋值串联列表会发生此错误。SELECT
例如,
SET @V = @W + @X + @Y + @Z
被绑定到如下形式的树中:前两个元素之后的每个连接元素都会在此表示中产生额外的嵌套级别。
SQL Server 可用的堆栈空间量决定了此嵌套的最终限制。当超出限制时,会在内部引发异常,最终导致如上所示的错误消息。抛出错误时的示例进程调用堆栈如下所示:
复制品
由于内部处理多个串联的方式,这是一个基本限制。它同样影响
SET
和SELECT
变量赋值语句。解决方法是限制在单个语句中执行的连接数。这通常也会更有效,因为编译深度查询树是资源密集型的。
受@Paul回答的启发,我做了一些研究,发现虽然堆栈空间确实限制了连接的数量,而且堆栈空间是可用内存的函数,因此会有所不同,但以下两点也是正确的:
首先,我修改了 Paul 的测试代码来连接字符串:
通过这个测试,我在不太好的笔记本电脑(只有 6 GB 的 RAM)上运行时可以获得的最高值是:
在收到错误8631之前。
接下来,我尝试使用括号对串联进行分组,以便该操作将串联多组串联。例如:
这样做我能够远远超出之前 3312 和 3513 变量的限制。更新后的代码是:
最大值(对我来说)现在
42
用于第一个REPLICATE
,因此每组使用 43 个变量,然后762
用于第二个REPLICATE
,因此使用 762 组,每组 43 个变量。初始组使用两个变量进行硬编码。@S
现在的输出显示变量中有 32,768 个字符。如果我将初始组更新为(@A+@A+@A)
而不是 just(@A+@A)
,则会收到以下错误:请注意,错误编号与以前不同。现在是:8632。而且,无论我使用 SQL Server 2012 实例还是 SQL Server 2017 实例,我都有同样的限制。
这里的上限——32,768——是(在 .NET 中)IF的最大容量(最大值为 32,767,但许多/大多数编程语言中的数组是从 0 开始的),这可能并非巧合。
SMALLINT
Int16
0
现在,这只是内存不足,换句话说,因为存储过程的操作在内存中完成,并且 SQL 可用的可用硬件晶体管或虚拟页面内存已满!
所以它基本上是 SQL Server 中的 Stack Overflow。
现在,首先尝试简化流程,因为我们知道您需要 1609 个变量,
但是您是否同时需要所有变量?
我们可以在需要的地方声明和使用变量。
例如:
但是如果我们通过添加来循环尝试这个
注意:这将使用更多的 CPU,并且需要更多的计算时间。
现在这会很慢,但具有更少的内存使用量的优势。
我希望这会有所帮助,请发布您的查询,以便我们了解确切的情况。
使用 SELECT 语句而不是 SET 可以提高性能和可读性,并且可以让您绕过所述错误。所以而不是:
你可以做:
并在一个语句中设置所有三个值。