Sql 总行已作出回应并提出建议。其中许多我将继续使用,在使用数据库时请记住。

Sql 总行已作出回应并提出建议。其中许多我将继续使用,在使用数据库时请记住。,sql,sql-server,performance,stored-procedures,sql-server-2012,Sql,Sql Server,Performance,Stored Procedures,Sql Server 2012,假设您没有陷入使用不同数据类型来寻找主表的索引的陷阱(即,您的PackageID是varchar,但不是nvarchar或numeric),那么表联接本身就非常快 要确认这一点,您可以将流程分为两个步骤,插入临时表,然后使用临时表进行连接。如果第一步是超慢的,第二步是超快的,那么这肯定了我上面的假设 如果第1步很慢,这意味着性能慢的主要原因是使用大量子字符串调用的拆分。 假设您的列表中每个ID有10000个20字节的项目。这意味着您有一个200KB的变量。 通过当前的子字符串调用,每次迭代时它都

假设您没有陷入使用不同数据类型来寻找主表的索引的陷阱(即,您的PackageID是varchar,但不是nvarchar或numeric),那么表联接本身就非常快

要确认这一点,您可以将流程分为两个步骤,插入临时表,然后使用临时表进行连接。如果第一步是超慢的,第二步是超快的,那么这肯定了我上面的假设

如果第1步很慢,这意味着性能慢的主要原因是使用大量子字符串调用的拆分。 假设您的列表中每个ID有10000个20字节的项目。这意味着您有一个200KB的变量。 通过当前的子字符串调用,每次迭代时它都会将200KB复制到一个新字符串中。字符串将从200KB逐渐减少到0KB,但您已经复制了5000次100+KB的字符串。这总共是1000MB的数据流

以下是3个功能。
[Split$60769735$0]是您的原始功能
[Split$60769735$1]正在使用XML
[Split$60769735$2]使用二进制分割,但也可以使用原始函数

[Split$60769735$1]速度很快,因为它利用了专门的XML解析器,该解析器已经能够很好地处理Split。 [Split$60769735$2]速度很快,因为它将O(n^2)复杂性更改为O(n log n)

运行时间为:
[拆分$60769735$0]=3到4分钟
[Split$60769735$1]=2秒
[Split$60769735$2]=7秒

注意:由于这是为了演示,一些边缘案例尚未处理。
1.对于[Split$60769735$1],如果值可能包含<>&,则需要一些转义
2.对于[Split$60769735$2],如果在字符串的后半部分找不到分隔符(即一个子项可以长达5000个字符),则需要处理charindex函数不返回命中的情况

创建模式[尝试]
去
创建函数[TRY]。[Split$60769735$0]
( 
@字符串VARCHAR(最大值),
@分隔符VARCHAR(5)
) 
返回@SplittedValues表
( 
事件ID内部标识(1,1),
SplitValue VARCHAR(最大值)
) 
作为
开始
声明@SplitLength INT
而LEN(@String)>0
开始
选择@SplitLength=(大小写字符索引(@Delimiter,@String)
当为0时,则为LEN(@String)
ELSE CHARINDEX(@Delimiter,@String)-1
(完)
插入@SplittedValues
选择子字符串(@String,1,@SplitLength)
选择@String=(大小写(LEN(@String)-@SplitLength)
当0时,则为“”
ELSE RIGHT(@String,LEN(@String)-@SplitLength-1)
(完)
结束
返回
结束
去
CREATE函数[TRY]。[Split$60769735$1]
( 
@字符串VARCHAR(最大值),
@分隔符VARCHAR(5)
) 
返回@SplittedValues表
( 
事件ID内部标识(1,1),
SplitValue VARCHAR(最大值)
) 
作为
开始
将@x XML=cast(“”+replace(@String,@Delimiter,)+“”声明为XML)
插入@SplittedValues
从@x.nodes('i')中选择v.value('.','varchar(100)')作为x(v)
返回
结束
去
CREATE函数[TRY]。[Split$60769735$2]
( 
@字符串VARCHAR(最大值),
@分隔符VARCHAR(5)
) 
返回@SplittedValues表
( 
事件ID内部标识(1,1),
SplitValue VARCHAR(最大值)
) 
作为
开始
声明@len int=len(@String);
如果@len>10000开始
声明@mid int=charindex(@Delimiter,@String,@len/2);
插入@SplittedValues
从TRY中选择SplitValue。[Split$60769735$2](子字符串(@String,1,@mid-1),@Delimiter);
插入@SplittedValues
从TRY中选择SplitValue。[Split$60769735$2](子字符串(@String、@mid+len(@Delimiter)、@len-@mid-len(@Delimiter)+1)、@Delimiter);
结束,否则开始
插入@SplittedValues
从TRY中选择SplitValue。[Split$60769735$0](@String,@Delimiter);
结束
返回
结束
去
注:
-从SQL Server 2016开始,将有一个内置的拆分功能。但不幸的是,你是在2012年


如果步骤1很快,但步骤2很慢,则可能存在数据类型不匹配或缺少索引的问题。在这种情况下,公布你的执行计划最有帮助。

这不是一个答案,而是一份清单。我看不出这个查询性能差的明显原因。以下是一些不太可能、真的不太可能和荒谬的可能性

+1关于“确保联接两侧的数据类型相同”

+1将“拆分”数据加载到其自己的临时表中时

我建议使用主键(而不是@temp)构建一个#temp表,原因不明,与统计数据有关,我认为这些统计数据在SQL Server的更高版本中不再相关(我从7.0开始,很容易忘记添加了大量的新式LED)

查询计划显示了什么

试着在“设置统计io打开”的情况下运行它,以查看实际涉及到多少页面读取

在测试期间,您确定这是针对该数据库运行的唯一查询吗

“我的桌子”是一张桌子,对吗?不是视图、同义词、链接服务器怪物或其他奇怪的构造

是否安装了任何第三方工具来记录数据库和/或服务器上的每个操作

假设PackageId在MyTable中不是唯一的,那么实际返回了多少数据?很可能只是读取数据并将其传回调用系统所需的时间太长了,尽管这似乎不太可能,除非服务器上还有大量其他工作要做。

创建一个#temp或@temp表(splitvalue主键群集)。。将dbo.Split()中不同的splitvalue插入临时表并连接Mytable。具有