【问题标题】:XPath in T-SQL queryT-SQL 查询中的 XPath
【发布时间】:2012-09-28 12:35:55
【问题描述】:

我有两张桌子,XMLtablefilterTable

我需要来自XMLtable 的所有XMLtable.ID 值,其中Col_X 中的数据包含MyElement,其内容与filterTable 中的filterColumn 匹配。

Col_X 中每一行的 XML 可能包含多个 MyElement,我希望 ID 以防这些元素中的任何一个与 filterColumn 中的任何值匹配。

问题在于这些列实际上是varchar(max) 数据类型,并且表本身很大(例如50GB 巨大)。所以这个查询需要尽可能的优化。

这是我现在所在位置的示例,它仅返回第一个匹配元素等于我正在寻找的元素之一的行。由于过多的不同错误消息,我似乎无法更改它以与我想要的所有相同命名的元素进行比较。

SELECT ID, 
   CAST(Col_X AS XML).value('(//*[local-name()=''MyElement''])', N'varchar(25)') 
FROM  XMLtable

...然后将结果与filterTable 进行比较。这已经需要 5 多分钟了。

我想要达到的目标是:

SELECT ID
FROM XMLtable
WHERE CAST(Col_X AS XML).query('(//*[local-name()=''MyElement''])') 
   IN (SELECT filterColumn FROM filterTable)

我目前能做到这一点的唯一方法是使用 LIKE 运算符,这需要大约一千倍的时间。

现在,显然不能开始更改列的数据类型或其他任何内容。这是我必须处理的。 :)

【问题讨论】:

  • 你能给我们展示一个示例 XML 并解释你想从中提取什么吗?
  • 我必须编一个,因为很明显我不能在这里给出真正的 XML。它是从几十到几百行各种元素的任何地方,其中 MyElement 可能出现在 0 到 X 次之间。我想找到它出现的行的 ID,并且这些出现的实例中的任何一个都包含另一个表中的任何值。

标签: sql-server xml tsql xpath sqlxml


【解决方案1】:

试试这个:

SELECT
  ID,
  MyElementValue
FROM 
  (
    SELECT ID, myE.value('(./text())[1]', N'VARCHAR(25)') AS 'MyElementValue'
    FROM XMLTable
      CROSS APPLY (SELECT CAST(Col_X AS XML)) as X(Col_X)
      CROSS APPLY X.Col_X.nodes('(//*[local-name()="MyElement"])') as T2(myE)
  ) T1
WHERE MyElementValue IN (SELECT filterColumn FROM filterTable)

还有这个:

SELECT
  ID,
  MyElementValue
FROM 
  (
    SELECT ID, myE.value('(./text())[1]', N'VARCHAR(25)') AS 'MyElementValue'
    FROM XMLTable
      CROSS APPLY (SELECT CAST(Col_X AS XML)) as X(Col_X)
      CROSS APPLY X.Col_X.nodes('//MyElement') as T2(myE)
  ) T1
WHERE MyElementValue IN (SELECT filterColumn FROM filterTable)

更新

我认为您正在经历这里描述的Compute Scalars, Expressions and Execution Plan Performance。对 XML 的强制转换推迟到对 value 函数的每次调用。您应该做的测试是将Col_X的数据类型更改为XML

如果这不是一个选项,您可以从 XMLTable 将所需的行查询到具有 XML 列的临时表中,然后针对临时表执行上述查询,而无需转换为 XML。

CREATE TABLE #XMLTable
(
  ID int,
  Col_X xml
)

INSERT INTO #XMLTable(ID, Col_X)
SELECT ID, Col_X
FROM XMLTable

SELECT
      ID,
      MyElementValue
    FROM 
      (
        SELECT ID, myE.value('(./text())[1]', N'varchar(25)') AS 'MyElementValue'
        FROM #XMLTable
          CROSS APPLY Col_X.nodes('//MyElement') as T2(myE)
      ) T1
    WHERE MyElementValue IN (SELECT filterColumn FROM filterTable)


DROP TABLE #XMLTable

【讨论】:

  • 嘿,非常感谢!下次我们返回此脚本或有时间时,我一定会检查一下。较低的一个不会起作用,因为我们所有的 XML 都在每个元素上使用命名空间,并且无论它们是什么,都可能因基础文档而异。这就是我在那里使用本地名称的原因。但除此之外,我总是对进一步优化感兴趣,所以谢谢! :)
  • @Kahn - 重要的变化是我使用(./text())[1] 来获取值,而不仅仅是.。它对查询计划中的估计成本有巨大的影响。如果它对执行时间有相同的影响,您必须对其进行测试。查询的重组可能会有所帮助,因为这只获取 MyElementValue 的值一次而不是两次。
  • 哦,对,不知道。我是 xpath 的新手,所以我不知道它如何工作的复杂性。所以解释是赞赏。 :)
  • 当我们需要在 SQL Server 中进行更多 XML/Xpath 查询时,现在回到这一点。不幸的是,我似乎没有比下面的其他变体获得更多的性能。
【解决方案2】:

你可以试试这样的。我相信,它至少在功能上可以满足您的需求。您必须根据经验使用您的数据集探索其性能。

SELECT ID
FROM 
( 
   SELECT xt.ID, CAST(xt.Col_X AS XML) [content] FROM XMLTable AS xt
) AS src
INNER JOIN FilterTable AS f
ON f.filterColumn IN 
(
   SELECT 
      elt.value('.', 'varchar(25)')     
   FROM src.content.nodes('//MyElement') AS T(elt)
)

【讨论】:

  • 嘿,谢谢。但是当我让它工作时,它的表现是我让它运行了 2.5 小时并且不得不取消。尽管非常感谢,但它确实给了我关于如何进行的想法!
  • 我发现添加一个基于使用 CHARINDEX 对过滤器值(在原始 varchar 列中)进行简单字符串搜索的附加 JOIN 条件有助于优化器将 XML 解析限制在更小的人群中行。这导致我的测试中的查询速度提高了一个数量级。
【解决方案3】:

我终于得到了这个工作,并且性能比我预期的要好得多。以下是最终在 5 - 6 分钟内产生正确结果的脚本。

SELECT ID, myE.value('.', N'VARCHAR(25)') AS 'MyElementValue'
FROM (SELECT ID, CAST(Col_X AS XML) AS Col_X
    FROM XMLTable) T1
CROSS APPLY Col_X.nodes('(//*[local-name()=''MyElement''])' T2(myE)
WHERE myE.value('.', N'varchar(25)') IN (SELECT filterColumn FROM filterTable)

感谢大家的帮助!

【讨论】:

  • 如果添加基于 CHARINDEX 的联接(请参阅我对自己的答案的评论)是否会给您带来任何进一步的显着改进,将会很有趣。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-06
  • 1970-01-01
  • 1970-01-01
  • 2021-12-05
  • 1970-01-01
  • 2022-01-15
相关资源
最近更新 更多