SqlServer StringToTable性能测试
问题起因: 最近做的项目DB数据量比较大(基本上一个月的数据就是10亿),而工程中Proc参数中包含有id拼接字符串,id拼接字符串格式:1,2,4,5,100,301。当数据量很小的情况下,这样做没有问题,但一旦数据量到达亿级,运行会很耗时,比如:当这样的参数id拼接
- 问题起因:
最近做的项目DB数据量比较大(基本上一个月的数据就是10亿),而工程中Proc参数中包含有id拼接字符串,id拼接字符串格式:1,2,4,5,100,301。当数据量很小的情况下,这样做没有问题,但一旦数据量到达亿级,运行会很耗时,比如:当这样的参数id拼接字符串中包含有10万个id的时候(我们实际应用中确实有这么多个id需要传到数据库,而且这样的id是从库中取出后,又经过程序的筛选后剩余的id),像这样的语句:
<span>Declare</span> <span>@IDS</span> <span>nvarchar</span>(<span>max</span><span>); </span><span>Set</span> <span>@IDS</span><span>=</span><span>'</span><span>10w个id用逗号分割组成的字符串</span><span>'</span><span>; </span><span>Select</span> T10.<span>TEXT</span>,T10.Name <span>FROM</span> DX.M <span>as</span> T10 <span>inner</span> <span>join</span> dbo.StringToTable(<span>@IDS</span>,<span>'</span><span>,</span><span>'</span>) <span>as</span> T11 <span>on</span> T10.ID<span>=</span>T11.ID;
执行了18个小时还未查询出数据。
备注:
虚拟机配置:内存:64G;CPU核数:40。
- DBA建议:
我测试了下,性能还算可以。在解析5000个逗号之内性能还行,太多了,性能就急速下降了。
最初的那个版本其实还是很常用的,性能要比改写之后的要好一些(在字符串特别长的情况下)。但是同样存在,如果字符串太长,性能急速下降的问题。
如果真的有5W以上逗号的字符串。这个SqlServer在执行计划上会消耗很多性能。
(自己也可以测试一下解析5000个逗号串和解析5W个字符串的差距,并不是5000字符串消耗时间*10的线性关系)
所以应当写一个循环,一次处理一部分。
比如以下两种方法:
1. 每次截取前1W个字符串,解析出来之后插入到临时表,然后在解析后面的,在插入到临时表,循环处理。最后临时表和实际表进行关联。
insert into #t1
select id
from dbo.stringtotable(@字符串1)
insert into #t1
select id
from dbo.stringtotable(@字符串2)
2。用in的方式,每次where条件 in 一部分。然后将结果union all起来。
类似如下
select id
from table a
where id in (@字符串1)
union all
select id
from table a
where id in (@字符串2)
两种方法都可行。在字符串较短的情况下,第二种方法应该好一些。字符串较长,第一种应该好一些。
- 测试代码:
<span>Declare</span> <span>@MRE_MROOIDS</span> <span>Nvarchar</span>(<span>Max</span><span>); </span><span>Set</span> <span>@MRE_MROOIDS</span><span>=</span><span>'</span><span>2,4,5,396009,</span><span>'</span><span>; </span><span>--</span><span>Set @MRE_MROOIDS='2,4,5,6,7,8,9,10,11,14,15,16,17,18,20,21,23,24,25,26,29,30';</span> <span>Declare</span> <span>@SplitChar</span> <span>nvarchar</span>(<span>2</span><span>); </span><span>Declare</span> <span>@EndIndex</span> <span>int</span><span>; </span><span>Declare</span> <span>@Step</span> <span>int</span><span>; </span><span>Declare</span> <span>@LastChars</span> <span>nvarchar</span>(<span>MAX</span><span>); </span><span>Declare</span> <span>@CurrentTempChars</span> <span>nvarchar</span>(<span>max</span><span>); </span><span>Set</span> <span>@LastChars</span><span>=</span><span>@MRE_MROOIDS</span><span>; </span><span>Set</span> <span>@Step</span><span>=</span><span>5000</span><span>; </span><span>Set</span> <span>@EndIndex</span><span>=</span><span>0</span><span>; </span><span>Set</span> <span>@SplitChar</span><span>=</span><span>'</span><span>,</span><span>'</span><span>; </span><span>IF</span> <span>EXISTS</span>(<span>SELECT</span> <span>*</span> <span>FROM</span> tempdb.dbo.sysobjects <span>where</span> id<span>=</span><span>OBJECT_ID</span>(N<span>'</span><span>tempdb..#StringToTableEntry_Temp10</span><span>'</span><span>)) </span><span>Begin</span> <span>Drop</span> <span>Table</span><span> #StringToTableEntry_Temp10; </span><span>End</span> <span>Create</span> <span>Table</span> #StringToTableEntry_Temp10(ID <span>INT</span><span>); </span><span>While</span>(<span>LEN</span>(<span>@LastChars</span>)<span>></span><span>@Step</span><span>) </span><span>Begin</span> <span>Set</span> <span>@EndIndex</span><span>=</span> <span>charindex</span>(<span>@SplitChar</span>,<span>@LastChars</span>,<span>@Step</span><span>); </span><span>Set</span> <span>@CurrentTempChars</span><span>=</span><span>SubString</span>(<span>@LastChars</span>,<span>0</span>,<span>@EndIndex</span><span>); </span><span>--</span><span> insert into temp table</span> <span>Insert</span> <span>Into</span><span> #StringToTableEntry_Temp10 </span><span>Select</span> Id <span>from</span> dbo.StringToTable2(<span>@CurrentTempChars</span>,<span>'</span><span>,</span><span>'</span><span>); </span><span>Set</span> <span>@LastChars</span><span>=</span><span>SubString</span>(<span>@LastChars</span>,<span>@EndIndex</span><span>+</span><span>1</span>,<span>LEN</span>(<span>@LastChars</span>)<span>-</span><span>@EndIndex</span><span>+</span><span>1</span><span>) </span><span>--</span><span>Select @LastChars as LastChars;</span> <span>Set</span> <span>@EndIndex</span><span>=</span><span>@EndIndex</span><span>+</span><span>@Step</span><span>; </span><span>End</span> <span>If</span> <span>LEN</span>(<span>@LastChars</span>)<span>></span><span>0</span> <span>Begin</span> <span>Insert</span> <span>Into</span><span> #StringToTableEntry_Temp10 </span><span>Select</span> Id <span>from</span> dbo.StringToTable2(<span>@LastChars</span>,<span>'</span><span>,</span><span>'</span><span>); </span><span>End</span> <span>Select</span> <span>COUNT</span>(<span>0</span>) <span>From</span> #StringToTableEntry_Temp10
StringToTable2函数:
<span>ALTER</span> <span>FUNCTION</span> <span>[</span><span>dbo</span><span>]</span>.<span>[</span><span>StringToTable</span><span>]</span><span> ( </span><span>@ids</span> <span>[</span><span>nvarchar</span><span>]</span>(<span>max</span><span>), </span><span>@separator</span> <span>[</span><span>char</span><span>]</span>(<span>1</span><span>) ) </span><span>RETURNS</span> <span>@IdsTable</span> <span>TABLE</span><span> ( </span><span>[</span><span>Id</span><span>]</span> <span>INT</span> <span>NOT</span> <span>NULL</span><span> ) </span><span>AS</span> <span>BEGIN</span> <span>IF</span>(<span>RIGHT</span>(<span>@ids</span>,<span>1</span>)<span>=</span><span>@separator</span><span>) </span><span>BEGIN</span> <span>SET</span> <span>@ids</span><span>=</span><span>SUBSTRING</span>(<span>@ids</span>,<span>0</span>,<span>LEN</span>(<span>@ids</span><span>)); </span><span>END</span> <span>--</span><span>下面的方式性能更好</span> <span>IF</span>(<span>LEN</span>(<span>@ids</span>) <span>></span> <span>0</span><span>) </span><span>BEGIN</span> <span>DECLARE</span> <span>@i</span> <span>int</span><span>; </span><span>SET</span> <span>@i</span> <span>=</span> <span>CHARINDEX</span>(<span>@separator</span>, <span>@ids</span><span>); </span><span>WHILE</span> <span>@i</span> <span>></span> <span>0</span> <span>BEGIN</span> <span>INSERT</span> <span>@IdsTable</span> <span>VALUES</span>(<span>LEFT</span>(<span>@ids</span>, <span>@i</span> <span>-</span> <span>1</span><span>)); </span><span>SET</span> <span>@ids</span> <span>=</span> <span>SUBSTRING</span>(<span>@ids</span>, <span>@i</span> <span>+</span> <span>1</span>, <span>LEN</span>(<span>@ids</span>) <span>-</span> <span>@i</span><span>); </span><span>SET</span> <span>@i</span> <span>=</span> <span>CHARINDEX</span>(<span>@separator</span>, <span>@ids</span><span>); </span><span>END</span> <span>IF</span>(<span>LEN</span>(<span>@ids</span>) <span>></span> <span>0</span><span>) </span><span>BEGIN</span> <span>INSERT</span> <span>@IdsTable</span> <span>VALUES</span>(<span>@ids</span><span>); </span><span>END</span> <span>END</span> <span>RETURN</span><span>; </span><span>END</span>
- 测试结果:
@MRE_MROOIDS包含id记录 |
@Step长度 |
执行时间 |
100,000 |
100000 |
00:09:15 |
100,000 |
20000 |
00:03:48 |
100,000 |
10000 |
00:01:57 |
100,000 |
5000 |
00:01:01 |

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

热门话题

MySQL 和 SQL Server 的语法差异主要体现在数据库对象、数据类型、SQL 语句和其他方面。数据库对象差异包括存储引擎和文件组的指定方式、索引和约束的创建。数据类型差异涉及数值类型、字符类型和日期时间类型的差异。SQL 语句差异体现在结果集限制、数据插入、更新和删除操作等方面。其他差异还包括标识列、视图和存储过程的创建方式。了解这些差异对于使用不同的数据库系统时避免错误非常重要。

新派幻想仙侠MMORPG《诛仙2》“无为测试”即将于4月23日开启,在原著千年后的诛仙大陆,会发生怎样的全新仙侠冒险故事?六境仙侠大世界,全日制修仙学府,自由自在的修仙生活,仙界中的万般妙趣都在等待着仙友们亲自前往探索!“无为测试”预下载现已开启,仙友们可前往官网下载,开服前无法登录游戏服务器,激活码可在预下载安装完成后使用。《诛仙2》“无为测试”开放时间:4月23日10:00——5月6日23:59诛仙正统续作全新仙侠冒险篇章《诛仙2》以《诛仙》小说为蓝图,在继承原著世界观的基础上,将游戏背景设

不同Java框架的性能对比:RESTAPI请求处理:Vert.x最佳,请求速率达SpringBoot2倍,Dropwizard3倍。数据库查询:SpringBoot的HibernateORM优于Vert.x及Dropwizard的ORM。缓存操作:Vert.x的Hazelcast客户机优于SpringBoot及Dropwizard的缓存机制。合适框架:根据应用需求选择,Vert.x适用于高性能Web服务,SpringBoot适用于数据密集型应用,Dropwizard适用于微服务架构。

PHP数组键值翻转方法性能对比表明:array_flip()函数在大型数组(超过100万个元素)下比for循环性能更优,耗时更短。手动翻转键值的for循环方法耗时相对较长。

Navicat 数据库配置文件的存储位置因操作系统而异:Windows:用户特定路径为 %APPDATA%\PremiumSoft\Navicat\macOS:用户特定路径为 ~/Library/Application Support/Navicat\Linux:用户特定路径为 ~/.config/navicat\配置文件名称包含连接类型,如 navicat_mysql.ini。这些配置文件存储数据库连接信息、查询历史和 SSH 设置。

优化C++多线程性能的有效技术包括:限制线程数量,避免争用资源。使用轻量级互斥锁,减少争用。优化锁的范围,最小化等待时间。采用无锁数据结构,提高并发性。避免忙等,通过事件通知线程资源可用性。

函数测试通过黑盒和白盒测试验证函数功能,而代码覆盖率衡量了测试用例覆盖的代码部分。不同语言(如Python和Java)的测试框架、覆盖率工具和特性不同。实战案例展示了如何使用Python的Unittest和Coverage以及Java的JUnit和JaCoCo进行函数测试和覆盖率评估。

Navicat连接URL格式为:协议://用户名:密码@主机:端口/数据库名称?参数,包含了连接所需的信息,包括协议、用户名、密码、主机名、端口、数据库名称和可选参数。
