sql-server - XML 查询非常慢,当 XML 类型的 .value 方法包含属性过滤器时
问题描述
我有一个 XML 值。它包含相当多的 XML,大约 13 MB。该文件在https://www.profinfo.pl/export/bookseller.xml公开可用 。事实上,它包含一个无效字符,但我在将其传递给 SQL Server 之前删除了所有这些字符。
该文件包含以下 XML 结构:
<?xml version="1.0" encoding="UTF-8"?>
<products>
<product>
<productId><![CDATA[108316]]></productId>
<productCode><![CDATA[PIP-6500:201903]]></productCode>
<productDateAdd><![CDATA[2019-03-15 14:54:52]]></productDateAdd>
<productDateUpdate><![CDATA[2019-03-15 15:07:20]]></productDateUpdate>
<productPrice><![CDATA[67.00]]></productPrice>
<productNettoPrice><![CDATA[62.04]]></productNettoPrice>
<productName><![CDATA[Państwo i Prawo - Nr 3/2019 [877]]]></productName>
<productState><![CDATA[]]></productState>
<productRateTax><![CDATA[8]]></productRateTax>
<productBarcode><![CDATA[]]></productBarcode>
<productUrl><![CDATA[https://www.profinfo.pl/sklep/panstwo-i-prawo-nr-32019-877,7255,r,2019,nr,3.html]]></productUrl>
<productImages>
<productImage><![CDATA[https://static.profinfo.pl/image/core_products/2019/3/15/a6e51ae4589402ae9e895c97eb42671e/admin/shop/0031-0980_877.jpg]]></productImage>
<productImage><![CDATA[https://static.profinfo.pl/image/core_products/2019/3/15/2b0c1bdae2ca13e745d2860428317e74/admin/shop/0031-0980_877.jpg]]></productImage>
<productImage><![CDATA[https://static.profinfo.pl/image/core_products/2019/3/15/ae609e0b6df358538f385fe4c668ab61/admin/shop/0031-0980_877.jpg]]></productImage>
</productImages>
<productCategories>
<productCategory ID="144"><![CDATA[Historia, teoria i filozofia prawa]]></productCategory>
</productCategories>
<productAuthors>
<productAuthor><![CDATA[Andrzej Wróbel]]></productAuthor>
</productAuthors>
<productAttributes>
<productAttribute name="Format"><![CDATA[]]></productAttribute>
<productAttribute name="Wydanie"><![CDATA[]]></productAttribute>
<productAttribute name="Medium"><![CDATA[Czasopismo papier]]></productAttribute>
<productAttribute name="Rodzaj"><![CDATA[]]></productAttribute>
<productAttribute name="Rok publikacji"><![CDATA[2019]]></productAttribute>
<productAttribute name="Spis treści"><![CDATA[<p><strong>TREŚĆ ZESZYTU 3/2019 „PAŃSTWA I PRAWA”</strong></p>]]></productAttribute>
<productAttribute name="Wydawnictwo"><![CDATA[Wolters Kluwer Polska]]></productAttribute>
<productAttribute name="Okładka"><![CDATA[]]></productAttribute>
<productAttribute name="Liczba stron"><![CDATA[]]></productAttribute>
<productAttribute name="Opis katalogowy 500"><![CDATA[]]></productAttribute>
<productAttribute name="Opis"><![CDATA[]]></productAttribute>
<productAttribute name="Stan prawny"><![CDATA[]]></productAttribute>
<productAttribute name="ISBN"><![CDATA[]]></productAttribute>
<productAttribute name="Wersja publikacji"><![CDATA[Czasopismo papier]]></productAttribute>
<productAttribute name="KodTowaru"><![CDATA[PIP-6500:201903]]></productAttribute>
<productAttribute name="Dostępność"><![CDATA[]]></productAttribute>
<productAttribute name="DrukNaŻyczenie"><![CDATA[0]]></productAttribute>
</productAttributes>
</product>
…
</products>
现在,想要在 SQL 中执行以下查询:
SELECT
product.value('productId[1]', 'VARCHAR(50)') AS id,
product.value('(productAttributes/productAttribute[@name="Format"])[1]', 'NVARCHAR(255)') AS format
FROM @xml.nodes('/products/product') p(product)
问题是,查询非常慢。我找到了克服这个限制的方法。我可以将此查询提取productAttributes
节点重写为单独的 XML 并仅查询这部分:
SELECT
product.value('productId[1]', 'VARCHAR(50)') AS id,
attrib.value('productAttribute[@name="Format"][1]', 'NVARCHAR(255)') AS format
FROM @xml.nodes('/products/product') p(product)
OUTER APPLY (SELECT product.query('productAttributes/productAttribute') AS attrib) a
但我仍然不明白为什么第一个查询的性能如此之低。
为了测试,我将返回数据的大小限制为前 100 行,并尝试比较两个查询的计划。
最可疑的片段是带有 XPath 过滤器的 XML Reader,它返回 3139900 行。
这等于 1847(product
整个文件中的元素数)乘以 17(productAttribute
每个产品中的元素数)乘以 100(顶部返回的行数)。不限制此函数将返回 1847 * 1847 * 17 = 57993953,我认为这是性能低下的原因。它随着 te XML 文件中的产品数量呈二次方增长。
有趣的是,第一个慢得多的查询的复杂性估计为批处理的 31%,第二个为 69%。
有谁知道为什么没有额外.query
方法的过滤如此昂贵?
解决方案
请尝试以下方法。
在我的机器上处理您的 13 MB XML 文件只需不到一秒的时间。我还需要删除某种特殊字符以使 XML 文件格式正确。
需要提及的要点:
- 我们正在将整个 XML 文件加载到具有一行的 SQL Server 表中。所有这些都不是将 XML 文件加载到内存中的 XML 类型的变量中。
OUTER APPLY
模拟LEFT OUTER JOIN
. 我们需要这个,因为 XML 有两个主要的层次结构(一对多)product-to-productAttributes。- 在幕后,SQL Server 在处理 XML 时会创建临时表。这就是为什么查询执行计划为每个显示带有 XML 阅读器的表值函数。
- 最佳实践是在 XQuery方法
text()
的 XML 元素的每个 XPath 表达式的末尾使用。.value()
它是 SQL Server XQuery 的特性。而且我没有看到你正在这样做。
SQL
DECLARE @tbl TABLE(
ID INT IDENTITY(1, 1) PRIMARY KEY,
XmlColumn XML
);
INSERT INTO @tbl(XmlColumn)
SELECT * FROM OPENROWSET(BULK N'c:\Temp\bookseller.xml', SINGLE_BLOB) AS x;
SELECT c.value('(productId/text())[1]', 'VARCHAR(10)') AS productId
, x.value('text()[1]', 'VARCHAR(255)') AS [Format]
FROM @tbl CROSS APPLY XmlColumn.nodes('/products/product') AS t1(c)
OUTER APPLY t1.c.nodes('productAttributes/productAttribute[@name="Format"]') AS t2(x);
部分输出
+-----------+--------+
| productId | Format |
+-----------+--------+
| 108008 | NULL |
| 108017 | NULL |
| 108316 | NULL |
| 108325 | NULL |
| 108574 | NULL |
| 108589 | A5 |
| 108595 | B5 |
+-----------+--------+
推荐阅读
- php - sql eloquent 模型中的 where 和 orderby 原因
- swift - 如何快速将具有相同对象的数组转换为单个对象?
- html - ERROR 错误:找不到带有路径的控件:'stocks -> stockName'
- sql - 在 Big Query 中制作连接表数组的最佳方法是什么?
- c# - 使用 EF Core 配置 Serilog ASP.Net Core 3.2 以记录 SQL 语句
- javascript - 导入时更改角度库的主题
- node.js - Mongoose 与 MongoDB 模式验证器
- java - Java android API 30 不在 ACTION_VIEW 中显示照片
- c++ - 访问 2d Apollonius 图 CGAL-加权 Voronoi 图的面和顶点
- netty - Netty 服务器无法获取客户端发送的所有消息