AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / dba / 问题 / 334386
Accepted
gotqn
gotqn
Asked: 2023-12-28 18:12:28 +0800 CST2023-12-28 18:12:28 +0800 CST 2023-12-28 18:12:28 +0800 CST

对于大字符串,“+”比“CONCAT”慢吗?

  • 772

我一直认为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 函数的最后一个参数,以便为连接生成更大的字符串。然后,我将每个变体执行三次。

sql-server
  • 1 1 个回答
  • 816 Views

1 个回答

  • Voted
  1. Best Answer
    Martin Smith
    2023-12-28T22:32:07+08:002023-12-28T22:32:07+08:00

    对于大字符串,“+”比“CONCAT”慢吗?

    看起来是这样。从查看tempdb为存储此 LOB 数据而分配和取消分配的页面来看,每个实例+最终都会创建一个新字符串并为其分配和取消分配页面tempdb(其中只需要最后一个)。

    而CONCAT仅使用最终结果大小所需的页面,并且不会因为+通过为(并写入)最终不需要的中间字符串分配页面而添加更多页面而降低性能。

    方法 新分配的页面 新释放的页面 新使用的页面
    + 249280 249280 0
    康卡特 99696 99696 0

    最终结果的数据长度是800000034字节(对于我下面的代码,其中包括额外的@V4串联)。该CONCAT方法分配了 99,696 个页面。平均每页 8024.4 字节。SQL Server 使用 8KB 页面,但需要一些空间用于页头开销。

    因为+您可以通过尝试下面的中间表达式来了解它是如何构建到这个数字的(每个步骤都使用合理数量的附加页面来确定其结果的数据长度,但这与前面步骤的页面使用情况一样,而不是相反)的)。

    表达 数据长度 分配的页数 差异 数据长度/差异
    CAST('' AS NVARCHAR(MAX)) + '{一些小文本}' 34 0 0
    CAST('' AS NVARCHAR(MAX)) + '{一些小文本}' + ISNULL(@V1, '{}') 200000034 24920 24920 8025.683547
    CAST('' AS NVARCHAR(MAX)) + '{一些小文本}' + ISNULL(@V1, '{}') + ISNULL(@V2, '{}') 400000034 74768 49848 8024.39484
    CAST('' AS NVARCHAR(MAX)) + '{一些小文本}' + ISNULL(@V1, '{}') + ISNULL(@V2, '{}') + ISNULL(@V3, '{}' ) 600000034 149552 74784 8023.107001
    CAST('' AS NVARCHAR(MAX)) + '{一些小文本}' + ISNULL(@V1, '{}') + ISNULL(@V2, '{}') + ISNULL(@V3, '{}' ) + ISNULL(@V4, '{}') 800000034 249280 99728 8021.81969

    正如大卫·布朗 - 微软在评论中所说......

    这是有意义的,因为+它是一个二元运算符,并且CONCAT是一个 N-ary函数。因此,无需特殊优化a+b+c即可评估为(a+b)+c,需要两个新字符串。虽然CONCAT(A,B,C)可以更轻松地构建一个新字符串

    在下面的脚本中,我没有分配给@R变量,因为这本身需要分配页面,并且我只关注串联方法使用的页面。

    DECLARE @internal_objects_alloc_page_count1 BIGINT, 
            @internal_objects_alloc_page_count2 BIGINT,
            @internal_objects_dealloc_page_count1 BIGINT, 
            @internal_objects_dealloc_page_count2 BIGINT
           ,@V1 NVARCHAR(MAX)
           ,@V2 NVARCHAR(MAX)
           ,@V3 NVARCHAR(MAX)
           ,@V4 NVARCHAR(MAX)
           ,@TestConcat BIT = 0;
    
    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)
           ,@V4 = REPLICATE(CAST('V4' AS NVARCHAR(MAX)), 50000000);
    
    SELECT @internal_objects_alloc_page_count1 = internal_objects_alloc_page_count, 
           @internal_objects_dealloc_page_count1 = internal_objects_dealloc_page_count
    from sys.dm_db_task_space_usage
    WHERE session_id = @@spid
    
    IF @TestConcat = 1
        SELECT DATALENGTH(CONCAT('{some small text}',ISNULL(@V1, '{}'), ISNULL(@V2, '{}'), ISNULL(@V3, '{}'), ISNULL(@V4, '{}')))
    ELSE
        SELECT DATALENGTH(CAST('' AS NVARCHAR(MAX)) + '{some small text}' + ISNULL(@V1, '{}') + ISNULL(@V2, '{}') + ISNULL(@V3, '{}') + ISNULL(@V4, '{}'));
    
    
    SELECT @internal_objects_alloc_page_count2 = internal_objects_alloc_page_count, 
           @internal_objects_dealloc_page_count2 = internal_objects_dealloc_page_count
    from sys.dm_db_task_space_usage
    WHERE session_id = @@spid
    
    
    SELECT @internal_objects_alloc_page_count2-@internal_objects_alloc_page_count1 AS newly_allocated_pages,
           @internal_objects_dealloc_page_count2-@internal_objects_dealloc_page_count1 AS newly_deallocated_pages,
           (@internal_objects_alloc_page_count2 - @internal_objects_dealloc_page_count2) - (@internal_objects_alloc_page_count1 - @internal_objects_dealloc_page_count1) as new_in_use_pages
    
    • 11

相关问题

  • SQL Server - 使用聚集索引时如何存储数据页

  • 我需要为每种类型的查询使用单独的索引,还是一个多列索引可以工作?

  • 什么时候应该使用唯一约束而不是唯一索引?

  • 死锁的主要原因是什么,可以预防吗?

  • 如何确定是否需要或需要索引

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目

    • 12 个回答
  • Marko Smith

    如何让sqlplus的输出出现在一行中?

    • 3 个回答
  • Marko Smith

    选择具有最大日期或最晚日期的日期

    • 3 个回答
  • Marko Smith

    如何列出 PostgreSQL 中的所有模式?

    • 4 个回答
  • Marko Smith

    列出指定表的所有列

    • 5 个回答
  • Marko Smith

    如何在不修改我自己的 tnsnames.ora 的情况下使用 sqlplus 连接到位于另一台主机上的 Oracle 数据库

    • 4 个回答
  • Marko Smith

    你如何mysqldump特定的表?

    • 4 个回答
  • Marko Smith

    使用 psql 列出数据库权限

    • 10 个回答
  • Marko Smith

    如何从 PostgreSQL 中的选择查询中将值插入表中?

    • 4 个回答
  • Marko Smith

    如何使用 psql 列出所有数据库和表?

    • 7 个回答
  • Martin Hope
    Jin 连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane 如何列出 PostgreSQL 中的所有模式? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh 为什么事务日志不断增长或空间不足? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland 列出指定表的所有列 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney MySQL 能否合理地对数十亿行执行查询? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx 如何监控大型 .sql 文件的导入进度? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison 你如何mysqldump特定的表? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 对 SQL 查询进行计时? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas 如何从 PostgreSQL 中的选择查询中将值插入表中? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 列出所有数据库和表? 2011-02-18 00:45:49 +0800 CST

热门标签

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve