Bog*_*nov 11 sql-server t-sql database-internals sql-server-2012 errors
我有一个包含业务逻辑的存储过程。在它里面我有大约 1609 个变量(不要问我为什么,这就是引擎的工作原理)。我尝试将SET一个变量连接到所有其他变量的连接值。结果在创建过程中出现错误:
消息 8631,级别 17,状态 1,过程 XXX,行 YYY 内部错误:已达到服务器堆栈限制。请在您的查询中寻找潜在的深层嵌套,并尝试简化它。
我发现错误是由于我需要在SET操作中使用的变量数量。我可以通过将其一分为二来执行任务。
我的问题是这方面有一些限制吗?我查了查,但没有找到。
我们检查了此 KB 中描述的错误,但这不是我们的情况。我们不在CASE代码中使用任何表达式。我们使用该临时变量来准备必须使用 CLR 函数替换的值列表。我们将 SQL Server 更新为 SP3 CU6(最新的),但我们仍然遇到错误。
Pau*_*ite 16
消息 8631,级别 17,状态 1,行 xxx
内部错误:已达到服务器堆栈限制。
请在您的查询中寻找潜在的深层嵌套,并尝试简化它。
由于 SQL Server 解析和绑定此类语句的方式 - 作为两个输入串联的嵌套列表,长SET或SELECT变量赋值串联列表会发生此错误。
例如,SET @V = @W + @X + @Y + @Z绑定到以下形式的树中:
ScaOp_Arithmetic x_aopAdd
ScaOp_Arithmetic x_aopAdd
ScaOp_Arithmetic x_aopAdd
ScaOp_Identifier @W
ScaOp_Identifier @X
ScaOp_Identifier @Y
ScaOp_Identifier @Z
Run Code Online (Sandbox Code Playgroud)
前两个之后的每个串联元素都会在此表示中产生额外的嵌套级别。
SQL Server 可用的堆栈空间量决定了此嵌套的最终限制。当超出限制时,会在内部引发异常,最终导致上面显示的错误消息。抛出错误时的示例进程调用堆栈如下所示:
DECLARE @SQL varchar(max);
SET @SQL = '
DECLARE @S integer, @A integer = 1;
SET @S = @A'; -- Change to SELECT if you like
SET @SQL += REPLICATE(CONVERT(varchar(max), ' + @A'), 3410) +';'; -- Change the number 3410
-- SET @S = @A + @A + @A...
EXECUTE (@SQL);
Run Code Online (Sandbox Code Playgroud)
由于内部处理多个串联的方式,这是一个基本限制。它影响SET和SELECT同样变量赋值语句。
解决方法是限制在单个语句中执行的串联次数。这通常也会更有效,因为编译深度查询树是资源密集型的。
受到@Paul的回答的启发,我做了一些研究,发现虽然堆栈空间确实限制了连接的数量,并且堆栈空间是可用内存的函数,因此会有所不同,但以下两点也是正确的:
首先,我改编了 Paul 的测试代码来连接字符串:
DECLARE @SQL NVARCHAR(MAX);
SET @SQL = N'
DECLARE @S VARCHAR(MAX), @A VARCHAR(MAX) = ''a'';
SET @S = @A';
SET @SQL += REPLICATE(CONVERT(NVARCHAR(MAX), N' + @A'), 3312) + N';';
-- SET @S = @A + @A + @A...
SET @SQL += N'SELECT DATALENGTH(@S) AS [Chars In @S];';
EXECUTE (@SQL);
Run Code Online (Sandbox Code Playgroud)
通过这个测试,我在不太好的笔记本电脑(只有 6 GB 的 RAM)上运行时可以获得的最高值是:
在收到错误8631之前。
接下来,我尝试使用括号对串联进行分组,这样操作将串联多组串联。例如:
SET @S = (@A + @A + @A + @A) + (@A + @A + @A + @A) + (@A + @A + @A + @A);
Run Code Online (Sandbox Code Playgroud)
这样做我能够远远超出 3312 和 3513 变量的先前限制。更新后的代码是:
DECLARE @SQL VARCHAR(MAX), @Chunk VARCHAR(MAX);
SET @SQL = '
DECLARE @S VARCHAR(MAX), @A VARCHAR(MAX) = ''a'';
SET @S = (@A+@A)';
SET @Chunk = ' + (@A' + REPLICATE(CONVERT(VARCHAR(MAX), '+@A'), 42) + ')';
SET @SQL += REPLICATE(CONVERT(VARCHAR(MAX), @Chunk), 762) + ';';
SET @SQL += 'SELECT DATALENGTH(@S) AS [Chars In @S];';
-- PRINT @SQL; -- for debug
-- SET @S = (@A+@A) + (@A + @A...) + ...
EXECUTE (@SQL);
Run Code Online (Sandbox Code Playgroud)
最大值(对我而言)现在42用于第一个REPLICATE,因此每组使用 43 个变量,然后762用于第二个REPLICATE,因此使用 762 个组,每组 43 个变量。初始组使用两个变量进行硬编码。
现在输出显示@S变量中有 32,768 个字符。如果我将初始组更新为 is(@A+@A+@A)而不是 just (@A+@A),则会出现以下错误:
消息 8632,级别 17,状态 2,行 XXXXX
内部错误:已达到表达式服务限制。请在您的查询中寻找潜在的复杂表达式,并尝试简化它们。
请注意,错误编号与以前不同。现在是:8632。而且,无论我使用 SQL Server 2012 实例还是 SQL Server 2017 实例,我都有同样的限制。
这可能不是巧合的是,这里的上限- 32,768 -是最大容量的SMALLINT(Int16.NET中)如果启动时0(最大值为32,767,但在许多/大多数编程语言中数组是从0开始)。
| 归档时间: |
|
| 查看次数: |
2783 次 |
| 最近记录: |