受@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);
通过此测试,在不那么出色的笔记本电脑(只有6 GB的RAM)上运行时,我可以获得的最高分数是:
- 使用SQL Server 2017 Express Edition LocalDB(14.0.3006)的3311(返回3312个总字符)
- 使用SQL Server 2012 Developer Edition SP4的3512(返回3513个总字符)(KB4018073)(11.0.7001)
在收到错误8631之前。
接下来,我尝试通过使用括号对串联进行分组,以便该操作将串联多个串联组。例如:
SET @S = (@A + @A + @A + @A) + (@A + @A + @A + @A) + (@A + @A + @A + @A);
这样做,我能够超越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);
现在,最大值(对我而言)将42
用于第一个REPLICATE
,因此每组使用43个变量,然后762
用于第二个REPLICATE
,因此使用762组,每组43个变量。初始组使用两个变量进行硬编码。
现在的输出显示该@S
变量中有32,768个字符。如果我将初始组更新为(@A+@A+@A)
而不是(@A+@A)
,则出现以下错误:
消息8632,级别17,状态2,第XXXXX行
内部错误:已达到表达式服务限制。请在查询中查找可能复杂的表达式,并尝试简化它们。
请注意,错误号与以前不同。现在是:8632。而且,无论我使用SQL Server 2012实例还是SQL Server 2017实例,我都有相同的限制。
这可能不是巧合的是,这里的上限- 32,768 -是最大容量的SMALLINT
(Int16
.NET中)如果启动时0
(最大值为32,767,但在许多/大多数编程语言中数组是从0开始)。