我一直认为CONCAT函数实际上是+(字符串连接)的包装,并带有一些额外的检查,以使我们的生活更轻松。
我还没有找到任何关于这些功能是如何实现的内部细节。至于性能,当数据在循环中连接时,调用似乎会产生开销CONCAT
(这似乎很正常,因为有额外的 NULL 句柄)。
几天前,一位开发人员修改了一些字符串连接代码(从+
到 ,CONCAT)
因为不喜欢语法并告诉我它变得更快。
为了检查情况,我使用了以下代码:
DECLARE @V1 NVARCHAR(MAX)
,@V2 NVARCHAR(MAX)
,@V3 NVARCHAR(MAX);
DECLARE @R NVARCHAR(MAX);
SELECT @V1 = REPLICATE(CAST('V1' AS NVARCHAR(MAX)), 50000000)
,@V2 = REPLICATE(CAST('V2' AS NVARCHAR(MAX)), 50000000)
,@V3 = REPLICATE(CAST('V3' AS NVARCHAR(MAX)), 50000000);
这是变体一:
SELECT @R = CAST('' AS NVARCHAR(MAX)) + '{some small text}' + ISNULL(@V1, '{}') + ISNULL(@V2, '{}') + ISNULL(@V3, '{}');
SELECT LEN(@R); -- 1200000017
这是变体二:
SELECT @R = CONCAT('{some small text}',ISNULL(@V1, '{}'), ISNULL(@V2, '{}'), ISNULL(@V3, '{}'))
SELECT LEN(@R); -- 1200000017
对于较小的字符串,没有差异。在某些时候,CONCAT
变体会变得更快:
我想知道有人可以分享一些内部结构或解释其行为,因为似乎可能存在一条规则,最好使用CONCAT
.
版本:
Microsoft SQL Server 2022 (RTM-CU8) (KB5029666) - 16.0.4075.1 (X64) 2023 年 8 月 23 日 14:04:50 版权所有 (C) 2022 Windows Server 2019 Standard 10.0(内部版本 17763)上的 Microsoft Corporation 标准版(64 位) :)(管理程序)
确切的脚本如下所示:
DECLARE @V1 NVARCHAR(MAX)
,@V2 NVARCHAR(MAX)
,@V3 NVARCHAR(MAX);
DECLARE @R NVARCHAR(MAX);
SELECT @V1 = REPLICATE(CAST('V1' AS NVARCHAR(MAX)), 50000000)
,@V2 = REPLICATE(CAST('V2' AS NVARCHAR(MAX)), 50000000)
,@V3 = REPLICATE(CAST('V3' AS NVARCHAR(MAX)), 50000000);
--SELECT @R = CAST('' AS NVARCHAR(MAX)) + '{some small text}' + ISNULL(@V1, '{}') + ISNULL(@V2, '{}') + ISNULL(@V3, '{}'); -- 00:00:45 -- 00:01:22 -- 00:01:20
--SELECT LEN(@R); -- 300000017
SELECT @R = CONCAT('{some small text}',ISNULL(@V1, '{}'), ISNULL(@V2, '{}'), ISNULL(@V3, '{}')) -- 00:00:11 -- 00:00:16 -- 00:00:10
SELECT LEN(@R); -- 300000017
我正在更改 REPLICATE 函数的最后一个参数,以便为连接生成更大的字符串。然后,我将每个变体执行三次。
看起来是这样。从查看
tempdb
为存储此 LOB 数据而分配和取消分配的页面来看,每个实例+
最终都会创建一个新字符串并为其分配和取消分配页面tempdb
(其中只需要最后一个)。而
CONCAT
仅使用最终结果大小所需的页面,并且不会因为+
通过为(并写入)最终不需要的中间字符串分配页面而添加更多页面而降低性能。最终结果的数据长度是800000034字节(对于我下面的代码,其中包括额外的
@V4
串联)。该CONCAT
方法分配了 99,696 个页面。平均每页 8024.4 字节。SQL Server 使用 8KB 页面,但需要一些空间用于页头开销。因为
+
您可以通过尝试下面的中间表达式来了解它是如何构建到这个数字的(每个步骤都使用合理数量的附加页面来确定其结果的数据长度,但这与前面步骤的页面使用情况一样,而不是相反)的)。正如大卫·布朗 - 微软在评论中所说......
在下面的脚本中,我没有分配给
@R
变量,因为这本身需要分配页面,并且我只关注串联方法使用的页面。