我的问题基于此:https://stackoverflow.com/q/35575990/5089204


要给出答案,我做了以下测试情况。

测试场景

首先,我创建一个测试表并用100.000行填充它。一个随机数(0到1000)应导致每个随机数约100行。此数字放入varchar列中,并作为XML中的值。

然后我像OP那样进行调用,需要使用.exist()和.nodes()进行调用,但好处不多第二次,但都需要5到6秒。实际上,我进行了两次呼叫:第二次以交换顺序进行,搜索参数略有变化,并使用“ // item”而不是完整路径,以避免通过缓存的结果或计划产生误报。

然后,我创建一个XML索引并进行相同的调用

现在-真正让我感到惊讶的是! -具有完整路径的.nodes比以前(9秒)要慢得多,但是.exist()可以降低到半秒,而完整路径甚至可以降低到约0.10秒。 (虽然.nodes()的路径较短,但更好,但仍远远落后于.exist())问题:

我自己的测试简而言之:XML索引会极大地破坏数据库。它们可以极大地加快处理速度(第2版),但也会降低查询速度。我想了解它们的工作原理...什么时候应该创建XML索引?为什么带有索引的.nodes()会比没有索引更糟糕?如何避免负面影响?

 CREATE TABLE #testTbl(ID INT IDENTITY PRIMARY KEY, SomeData VARCHAR(100),XmlColumn XML);
GO

DECLARE @RndNumber VARCHAR(100)=(SELECT CAST(CAST(RAND()*1000 AS INT) AS VARCHAR(100)));

INSERT INTO #testTbl VALUES('Data_' + @RndNumber,
'<error application="application" host="host" type="exception" message="message" >
  <serverVariables>
    <item name="name1">
      <value string="text" />
    </item>
    <item name="name2">
      <value string="text2" />
    </item>
    <item name="name3">
      <value string="text3" />
    </item>
    <item name="name4">
      <value string="text4" />
    </item>
    <item name="name5">
      <value string="My test ' +  @RndNumber + '" />
    </item>
    <item name="name6">
      <value string="text6" />
    </item>
    <item name="name7">
      <value string="text7" />
    </item>
  </serverVariables>
</error>');

GO 100000

DECLARE @d DATETIME=GETDATE()
SELECT #testTbl.*
FROM #testTbl
CROSS APPLY XmlColumn.nodes('/error/serverVariables/item[@name="name5" and value/@string="My test 600"]') AS a(b);
SELECT CAST(GETDATE()-@d AS TIME) AS NodesFullPath_no_index;
GO

DECLARE @d DATETIME=GETDATE();
SELECT * 
FROM #testTbl
WHERE XmlColumn.exist('/error/serverVariables/item[@name="name5" and value/@string="My test 600"]') = 1;
SELECT CAST(GETDATE()-@d AS TIME) AS ExistFullPath_no_index;
GO

DECLARE @d DATETIME=GETDATE();
SELECT * 
FROM #testTbl
WHERE XmlColumn.exist('//item[@name="name5" and value/@string="My test 500"]') = 1;
SELECT CAST(GETDATE()-@d AS TIME) AS ExistShortPath_no_index;
GO

DECLARE @d DATETIME=GETDATE()
SELECT #testTbl.*
FROM #testTbl
CROSS APPLY XmlColumn.nodes('//item[@name="name5" and value/@string="My test 500"]') AS a(b);
SELECT CAST(GETDATE()-@d AS TIME) AS NodesShortPath_no_index;
GO

CREATE PRIMARY XML INDEX PXML_test_XmlColum1 ON #testTbl(XmlColumn);
CREATE XML INDEX IXML_test_XmlColumn2 ON #testTbl(XmlColumn) USING XML INDEX PXML_test_XmlColum1 FOR PATH;
GO

DECLARE @d DATETIME=GETDATE()
SELECT #testTbl.*
FROM #testTbl
CROSS APPLY XmlColumn.nodes('/error/serverVariables/item[@name="name5" and value/@string="My test 600"]') AS a(b);
SELECT CAST(GETDATE()-@d AS TIME) AS NodesFullPath_with_index;
GO

DECLARE @d DATETIME=GETDATE();
SELECT * 
FROM #testTbl
WHERE XmlColumn.exist('/error/serverVariables/item[@name="name5" and value/@string="My test 600"]') = 1;
SELECT CAST(GETDATE()-@d AS TIME) AS ExistFullPath_with_index;
GO

DECLARE @d DATETIME=GETDATE();
SELECT * 
FROM #testTbl
WHERE XmlColumn.exist('//item[@name="name5" and value/@string="My test 500"]') = 1;
SELECT CAST(GETDATE()-@d AS TIME) AS ExistShortPath_with_index;
GO

DECLARE @d DATETIME=GETDATE()
SELECT #testTbl.*
FROM #testTbl
CROSS APPLY XmlColumn.nodes('//item[@name="name5" and value/@string="My test 500"]') AS a(b);
SELECT CAST(GETDATE()-@d AS TIME) AS NodesShortPath_with_index;
GO

DROP TABLE #testTbl;
 


编辑1-结果

这是在中型笔记本电脑上本地安装SQL Server 2012的结果
在此测试中,我无法再现对NodesFullPath_with_index的极端负面影响,尽管它比没有索引时要慢...

NodesFullPath_no_index    6.067
ExistFullPath_no_index    6.223
ExistShortPath_no_index   8.373
NodesShortPath_no_index   6.733

NodesFullPath_with_index  7.247
ExistFullPath_with_index  0.217
ExistShortPath_with_index 0.500
NodesShortPath_with_index 2.410


EDIT 2使用更大的XML进行测试

根据TT的建议,我使用了上面的XML,但是复制了item -nodes来达到大约450个项目。我在XML中让hit-node的位置很高(因为我认为.exist()会在第一次命中时停止,而.nodes()会继续)

创建XML索引会炸毁mdf-文件到〜21GB,〜18GB似乎属于索引(!!!)

NodesFullPath_no_index    3min44
ExistFullPath_no_index    3min39
ExistShortPath_no_index   3min49
NodesShortPath_no_index   4min00

NodesFullPath_with_index  8min20
ExistFullPath_with_index  8,5 seconds !!!
ExistShortPath_with_index 1min21
NodesShortPath_with_index 13min41 !!!


#1 楼

当然这里有很多事情要做,所以我们只需要看看这将导致什么。

首先,SQL Server 2012和SQL Server 2014之间的时间差异是由于SQL Server 2014中新增了基数估计器。您可以在SQL Server 2014中使用跟踪标志来强制使用旧的估计器然后您将在SQL Server 2014中看到与SQL Server 2012中相同的时序特征。

比较nodes()exist()是不公平的,因为如果存在多个匹配元素,它们将不会返回相同的结果在XML中为一行。无论如何,exist()都会从基表中返回一行,而nodes()可能会为您在基表中为每一行返回不止一行。
我们知道数据,但是SQL Server却不知道,因此必须建立查询计划要考虑到这一点。

要使nodes()查询等同于exist()查询,您可以执行以下操作。这样的查询在使用nodes()exist()之间没有区别,这是因为SQL Server为两个不使用索引的版本构建了几乎相同的计划,并且在使用索引时构建了完全相同的计划。对于SQL Server 2012和SQL Server 2014都是如此。

对我来说,在SQL Server 2012中,使用上面的nodes()查询的修改版本,没有XML索引的查询需要6秒钟。使用完整路径或短路径没有区别。有了XML索引,完整路径版本最快并且需要5毫秒,而使用短路径则需要大约500毫秒。检查查询计划将告诉您为什么有区别,但是简短的版本是,当您使用短路径时,SQL Server在短路径上的索引中查找(使用like进行范围查找)并返回700000行,然后丢弃这些行该值不匹配。使用完整路径时,SQL Server可以直接使用路径表达式与节点的值一起执行查找,并且仅从头开始返回105行以进行处理。

使用SQL Server 2014和新的基数估算器,使用XML索引时,这些查询没有区别。在不使用索引的情况下,查询所花费的时间仍然相同,但是是15秒。使用新材料时,这显然不是一种改进。

由于我将查询修改为等价的,所以不确定我是否完全忘记了您的问题的实质,但这就是我现在所相信的。


为什么使用XML索引的nodes()查询(原始版本)显着
比不使用索引时慢?


好吧,答案是SQL Server查询计划优化器做得不好,并且引入了假脱机运算符。我不知道为什么,但是好消息是,SQL Server 2014中新的基数估计器已不复存在。
没有索引,无论使用哪种基数估计器,查询都将花费大约7秒。使用索引时,旧的估算器(SQL Server 2012)需要15秒,而新的估算器(SQL Server 2014)大约需要2秒。

注意:以上发现对您的测试数据有效。可能有一个完全不同的故事来告诉您是否更改XML的大小,形状或形式。如果不对表中实际存在的数据进行测试,就无法确定。

XML索引如何工作

SQL Server中的XML索引是作为内部表实现的。主XML索引使用基表的主键加上节点ID列(总共12列)创建表。每个element/node/attribute etc.将有一行,因此根据存储的XML的大小,表当然可以变得很大。有了主XML索引后,SQL Server可以使用内部表的主键为基表中的每一行定位XML节点和值。辅助XML索引分为三种类型。创建二级XML索引时,内部表上会创建一个非聚集索引,并且根据您创建的二级索引的类型,它会具有不同的列和列顺序。

来自创建XML索引(Transact-SQL):



在主键为
(主值(节点值和路径))的列上创建辅助XML索引XML索引。

PATH
在基于路径值
和主XML索引中的节点值构建的列上创建辅助XML索引。在PATH二级索引中,
路径和节点值是关键列,可在搜索路径时实现高效查找。

PROPERTY
在以下位置创建二级XML索引主要XML索引的列(PK,路径和节点
值),其中PK是
基表的主键。


因此,当您创建PATH索引时,该索引中的第一列是路径表达式,第二列是该节点中的值。实际上,路径以一种压缩格式存储并反转。反向存储是使其在使用短路径表达式的搜索中有用的原因。在短路径情况下,您搜索了//item/value/@string//item/@name//item。由于路径是反向存储的,因此SQL Server可以对like = '€€€€€€%使用范围查找,其中€€€€€€是反向路径。使用完整路径时,没有必要使用like,因为整个路径都在列中进行了编码,并且该值也可以在查找谓词中使用。

您的问题:


什么时候应该创建XML索引?


万不得已。最好设计数据库,这样您就不必使用XML内的值在where子句中进行过滤。如果事先知道需要这样做,则可以使用属性提升来创建一个计算列,该索引可以在需要时进行索引。从SQL Server 2012 SP1开始,您还可以使用选择性XML索引。幕后的工作原理与常规XML索引几乎相同,只是您在索引定义中指定了路径表达式,并且仅对匹配的节点进行了索引。这样可以节省很多空间。


为什么带有索引的.nodes()会比没有索引更糟糕?


何时出现是在表上创建的XML索引,SQL Server将始终使用该索引(内部表)来获取数据。该决定是在优化程序对什么是快速和什么不是快速做出决定之前做出的。优化器的输入将被重写,以便它使用内部表,然后由优化器决定是否像常规查询一样尽力而为。如果不使用索引,则会使用几个表值函数。最重要的是,如果不进行测试,您将无法确定哪个会更快。


如何避免负面影响?


测试

评论


您对.nodes()和.exist()的区别的想法令人信服。具有全路径搜索的索引更快的事实似乎也很容易理解。这意味着:如果创建XML索引,则必须始终注意任何通用XPath(//或*或..或[filter]或不只是Xpath的任何其他东西)的负面影响。实际上,您应该只使用完整路径-相当不错的后退...

–Shnugo
16-2-26在20:31