在SQL Server中,我有一个带有XML类型临时变量的存储过程,我正在对该变量执行删除操作。当我在具有4个内核和6 GB RAM的本地VM中运行此存储过程时,执行需要24秒。但是当我在具有40个内核和128 GB RAM的服务器中运行相同的存储过程时,此删除语句执行时间超过38分钟。整个存储过程在此删除语句中被挂起38分钟。注释掉delete语句后,存储过程将在8秒内在服务器上执行。我如何解决这个性能问题。 SQL服务器配置有什么问题吗?
And this is the output i want.
SalesMonth SalesID openingBalance SalesTotal SalesReturn Indebtednes
---------- ------- -------------- ---------- ----------- ------------
1 1 352200 0 5600 346600
------------------------------------------------------------------------
1 2 50000 1100 0 51100
------------------------------------------------------------------------
1 3 9500 6000 0 15500
------------------------------------------------------------------------
2 1 0 0 1200 345400
------------------------------------------------------------------------
2 2 0 300 0 51400
------------------------------------------------------------------------
2 3 0 500 1000 15000
------------------------------------------------------------------------
3 1 0 600 0 346000
------------------------------------------------------------------------
3 2 0 200 0 51600
-----------------------------------------------------------------------
3 3 0 0 10 14990
-----------------------------------------------------------------------
.
.
.
12 1 0 0 0 NULL
----------------------------------------------------------------------
12 2 0 0 0 NULL
----------------------------------------------------------------------
12 3 0 0 0 NULL
@Mikael:下面是在服务器上粉碎成行解决方案的执行计划(具有40个内核和128 GB RAM) 以下是我本地VM中的执行计划(具有4个内核和6 GB RAM):
答案 0 :(得分:7)
在我的机器上,删除需要1小时25分钟,并且给了我不太好的查询计划。
此计划查找所有空节点(要删除的节点)并将其存储在表假脱机中。然后,对于整个文档中的每个节点,检查该节点是否存在于假脱机中(嵌套循环(左半连接)),以及是否该节点从最终结果中排除(合并连接(左反半连接)) )。然后从UDX运算符中的节点重建xml并将其分配给变量。表磁盘未编入索引,因此对于需要检查的每个节点,将扫描整个假脱机(或直到找到匹配)。
这实质上意味着此算法的性能为O(n*d)
,其中n
是节点总数,d
是总数或已删除节点。
有几种可能的解决方法。
首先,如果您可以修改XML查询以便不首先生成空节点,那么首先也许是最好的。完全可以使用for xml
创建XML,如果已经将部分XML存储在表中,则可能无法实现。
另一种选择是在Row
上粉碎XML(参见下面的示例XML),将结果放在表变量中,修改表变量中的XML,然后重新创建组合的XML。
declare @T table(PaymentData xml);
insert into @T
select T.X.query('.')
from @PaymentData.nodes('Row') as T(X);
update @T
set PaymentData.modify('delete //*[not(node())]');
select T.PaymentData as '*'
from @T as T
for xml path('');
这将为您提供O(n*s*d)
的性能特征,其中n
是row
个节点的数量,s
是每个{{1}的子节点数} node和row
是每个d
节点已删除行的数量。
我真的不建议使用的第三个选项是使用未记录的跟踪标志来删除计划中假脱机的使用。您可以在测试中试用它,也可以捕获生成的计划并在计划指南中使用它。
row
带有跟踪标志的查询计划:
而不是1小时25分钟,这个版本在我的电脑上花了4秒钟。
将XML分解为多个行到表变量总共需要6秒才能执行。
根本不必删除任何行当然是最快的。
示例数据,包含32个子节点的12000个节点,如果您想在家中尝试,则其中2个为空。
declare @T table(PaymentData xml);
insert into @T values(@PaymentData);
update @T
set PaymentData.modify('delete //*[not(node())]')
option (querytraceon 8690);
select @PaymentData = PaymentData
from @T;
注意:我不知道为什么在你的一台服务器上执行它只需要24秒。我建议你重新检查XML实际上是相同的。或者为什么不使用我为您提供的XML示例进行测试。
<强>更新强>
对于碎化版本,删除查询中的假脱机问题可能会移动到碎片查询,而不会给您带来大致相同的不良性能。然而,这并非总是如此。我已经看到没有线轴的计划和有线轴的计划,我不知道为什么它有时存在,为什么它不在其他时候。
我还发现,如果你使用临时表而不是declare @PaymentData as xml;
set @PaymentData = (
select top(12000)
1 as N1, 1 as N2, 1 as N3, 1 as N4, 1 as N5, 1 as N6, 1 as N7, 1 as N8, 1 as N9, 1 as N10,
1 as N11, 1 as N12, 1 as N13, 1 as N14, 1 as N15, 1 as N16, 1 as N17, 1 as N18, 1 as N19, 1 as N20,
1 as N21, 1 as N22, 1 as N23, 1 as N24, 1 as N25, 1 as N26, 1 as N27, 1 as N28, 1 as N29, 1 as N30,
'' as N31,
'' as N32
from sys.columns as c1, sys.columns as c2
for xml path('Row')
);
,我就不会在粉碎查询中获取假脱机。
insert ... into