【发布时间】:2015-08-18 21:04:20
【问题描述】:
我想创建一个子查询,它生成一个数字列表作为单列结果,类似于 MindLoggedOut did here 但没有 @x xml 变量,以便它可以附加到 WHERE 表达式为没有 sql 参数的纯字符串(子查询)。问题是参数(或变量)的替换使查询运行慢了 5000 倍,我不明白为什么。是什么导致了如此大的差异?
例子:
/* Create a minimalistic xml like <b><a>78</a><a>91</a>...</b> */
DECLARE @p_str VARCHAR(MAX) =
'78 91 01 12 34 56 78 91 01 12 34 56 78 91 01 12 34 56';
DECLARE @p_xml XML = CONVERT(XML,
'<b><a>'+REPLACE(@p_str,' ','</a><a>')+'</a></b>'
);
SELECT a.value('(child::text())[1]','INT')
FROM (VALUES (@p_xml)) AS t(x)
CROSS APPLY x.nodes('//a') AS x(a);
这每行返回一个数字并且速度非常快(比我目前使用的字符串拆分器方法快 20 倍,similartothese。
我在 sql server CPU 时间方面测量了 20 倍的加速,@p_str 包含 3000 个数字。)
现在如果我将@p_xml 的定义内联到查询中:
SELECT a.value('(child::text())[1]','INT')
FROM (VALUES (CONVERT(XML,
'<b><a>'+REPLACE(@p_str,' ','</a><a>')+'</a></b>'
))) AS t(x)
CROSS APPLY x.nodes('//a') AS x(a);
然后它变得慢了 5000 倍(当 @p_str 包含数千个数字时。)查看查询计划我找不到原因。
first query(…VALUES(@p_xml)…)和the second(…VALUES(CONVERT(XML,'...'))…)的计划
有人能解释一下吗?
更新
显然第一个查询的计划不包括成本
@p_xml = CONVERT(XML, ...REPLACE(...)... ) 的分配,但是这个
成本不是可以解释 46 毫秒与 234 秒的罪魁祸首
整个脚本的执行时间之间的差异(当
@p_str 很大)。这种差异是系统性的(不是随机的)
并且实际上是在 SqlAzure(S1 层)中观察到的。
此外,当我重写查询时:将CONVERT(XML,...) 替换为用户定义的标量函数:
SELECT a.value('(child::text())[1]','INT')
FROM (VALUES (dbo.MyConvertToXmlFunc(
'<b><a>'+REPLACE(@p_str,' ','</a><a>')+'</a></b>'
))) AS t(x)
CROSS APPLY x.nodes('//a') AS x(a);
dbo.MyConvertToXmlFunc() 在哪里:
CREATE FUNCTION dbo.MyConvertToXmlFunc(@p_str NVARCHAR(MAX))
RETURNS XML BEGIN
RETURN CONVERT(XML, @p_str);
END;
差异消失了 (plan)。所以至少我有一个解决方法......但想了解它。
【问题讨论】:
-
您能否发布 2 个查询之间的 sql 执行计划的图片,以便我们查看差异?
-
我怀疑这是因为第一个执行计划测量的少;即
@p_xml的分配(因此从字符串到 xml 的相关对话)不会出现在执行计划中,所以你不会像这样比较。 -
实际计划有什么不同吗?估计的行数有什么不同吗?
标签: sql-server xml performance sql-execution-plan