【问题标题】:XPath/XQuery - getting the field with namespace attributes out as a value/query?XPath/XQuery - 将具有命名空间属性的字段作为值/查询获取?
【发布时间】:2020-10-21 07:43:50
【问题描述】:

我有类似于以下代码的 XML。我的最终目标涉及更改一些节点并使用节点、存在、值剥离其他节点,然后使用 FOR XML PATH 重新创建这个新的 XML - 这一切都很好。

但是,我不知道如何只取回“次要”的行/属性,特别是 xmlns:xsi 和 xmlns:xsd)。

对于下面的示例,我如何在 Secondary 中获取命名空间属性,以便可以使用 FOR XML PATH 将其与调整后的 event/myfield/etc 结合起来?还是我需要写一个 FLWOR 来做到这一点? (如果是这样,至少在这部分有什么建议吗?)

我想要的,在最后:

<Secondary xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Secondary xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">

然后我可以将它与我正在使用 FOR PATH XML 的其他字段结合起来,并将其提供给下游。 如果更简单,您能否将 xmlns:xsi 和 xmlns:xsd 与“值”一起拉出并将其连接起来以使其看起来相同?

DECLARE @xml TABLE (id int IDENTITY, switch_xml XML)
INSERT INTO @xml (switch_xml)
VALUES ('<MAIN>
<Secondary xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <event>53</event>
  <myfield>a</myfield>
</Secondary>
<Secondary xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <event>56</event>
  <myfield>a</myfield>
</Secondary>
</MAIN>
')

SELECT 
--- ???? getting the Secondary here
Ev.Dat.query('event') 
FROM @xml X OUTER APPLY switch_xml.nodes('/MAIN/Secondary') AS Ev(Dat)

更多可重现的例子:

<MAIN>
    <Secondary xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <event>53</event>
      <myfield>string o text</myfield>
      <myfield2>some other string</myfield2>
    </Secondary>
    <Secondary xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <event>56</event>
      <myfield>different string o tet</myfield>
      <myfield2>and some other other strings</myfield2>
    </Secondary>
    </MAIN>

requested returned - 因为有一个事件 53,所以只删除事件 56 上的 myfield。如果没有事件 53 节点,您将不理会 56

<MAIN>
    <Secondary xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <event>53</event>
      <myfield>string o text</myfield>
      <myfield2>some other string</myfield2>
    </Secondary>
    <Secondary xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <event>56</event>
      <myfield></myfield>
      <myfield2>and some other other strings</myfield2>
    </Secondary>
    </MAIN>

我的那个查询的例子,它不处理命名空间: (现在写 - 这会得到节点,只需要正确包装它以将节点组合成一个 MAIN)

SELECT 
--- ???? getting the Secondary here
CONVERT(XML,(SELECT Ev.Dat.query('event') ,
CASE WHEN switch_xml.exist('/MAIN/EventData[event="56"]') = 1 AND ev.dat.value('(event)[1]','int') IN (53) THEN ev.dat.query('(myfield)[1]')
WHEN switch_xml.exist('/MAIN/EventData[event="56"]') = 0 AND ev.dat.value('(event)[1]','int') IN (53) THEN ev.dat.query('(myfield)[1]') else '' END
,Ev.Dat.query('myfield2') FOR XML PATH('Secondary')))  AS newxml
FROM @xml X OUTER APPLY switch_xml.nodes('/MAIN/Secondary') AS Ev(Dat)

【问题讨论】:

  • 你的最终目标是什么?你写 What I want, in end 的部分似乎只是 &lt;Secondary&gt; 开始标记。但仅此一项就不是格式良好的 XML...
  • @Shnugo 是的,这正是我想要摆脱的。实际的最终目标涉及根据其他字段过滤某些子节点中的字段(如果文档包含具有这些特定值的子节点,则重写其他子节点),但所有这些都有效 - 我只需要使用 xsi/xsd 进行辅助,我'将使用 FOR XML PATH 将它们组合在一起以创建 XML,然后继续我的方式。
  • 好吧,如果您提供minimal reproducible example 会很有帮助。一些样本作为输入和预期输出。我必须承认我不知道你真正想要什么。创建开始标签?那不是 XML,而只是看起来像 XML 的东西…… 操作现有的 XML?你的表现不足以帮助你解决这个问题......老实说,这听起来像是一个 XY 问题......
  • @Shnugo 添加。希望它能解释为什么我试图让原始请求“更简单”。
  • 查看您的代码,我看到 .exist('/MAIN/EventData[event="56"]') 指向不同的 XML,在上面的示例中没有 &lt;EventData&gt;...

标签: sql-server xml xquery


【解决方案1】:

感谢您向您的 XML 添加示例。

可能这并不能满足您的所有需求,但我认为您可能会走这条路:

注意:我假设每个 XML 只出现一次任何事件 ID。

您的样机表示例。我添加了两行,一行有事件 53,另一行没有。

DECLARE @xml TABLE (id int IDENTITY, switch_xml XML)
INSERT INTO @xml (switch_xml)
VALUES 
 ('<MAIN> <!-- event 53 exists -->
    <Secondary xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <event>53</event>
      <myfield>string o text</myfield>
      <myfield2>some other string</myfield2>
    </Secondary>
    <Secondary xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <event>56</event>
      <myfield>different string o tet</myfield>
      <myfield2>and some other other strings</myfield2>
    </Secondary>
    </MAIN>')

,(N'<MAIN> <!-- no event 53 here!!! -->
    <Secondary xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <event>50</event>
      <myfield>string o text</myfield>
      <myfield2>some other string</myfield2>
    </Secondary>
    <Secondary xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <event>56</event>
      <myfield>different string o tet</myfield>
      <myfield2>and some other other strings</myfield2>
    </Secondary>
    </MAIN>');

--使用参数让生活更轻松

DECLARE @SearchForEvent INT = 53;
DECLARE @ChangeThisEvent INT = 56;
DECLARE @ReplaceWith VARCHAR(100)='Some replacement';

--我们可以使用.modify()来更新需要的值

UPDATE @xml SET switch_xml.modify('replace value of (/MAIN[Secondary[event=sql:variable("@SearchForEvent")]]
                                                     /Secondary[event=sql:variable("@ChangeThisEvent")]
                                                     /myfield
                                                     /text())[1] with sql:variable("@ReplaceWith")');

--检查结果

SELECT * FROM @xml;

第一个 XML 如下所示:

<MAIN>
  <!-- event 53 exists -->
  <Secondary xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <event>53</event>
    <myfield>string o text</myfield>
    <myfield2>some other string</myfield2>
  </Secondary>
  <Secondary xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <event>56</event>
    <myfield>Some replacement</myfield>
    <myfield2>and some other other strings</myfield2>
  </Secondary>
</MAIN>

...第二个像这样

<MAIN>
  <!-- no event 53 here!!! -->
  <Secondary xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <event>50</event>
    <myfield>string o text</myfield>
    <myfield2>some other string</myfield2>
  </Secondary>
  <Secondary xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <event>56</event>
    <myfield>different string o tet</myfield>
    <myfield2>and some other other strings</myfield2>
  </Secondary>
</MAIN>

简而言之:

  • 我们通过 XPath 告诉 .modify() 要更改什么
  • 如果存在&lt;Secondary&gt;,Xpath 将深入到&lt;MAIN&gt;,它有一个&lt;event&gt; 元素,其值为给定(=53)
  • 如果这成立,我们将在&lt;Main&gt; 下方进入&lt;Secondary&gt;,搜索&lt;event&gt; 具有其他给定值的元素 (=56)
  • 如果找到,我们可以将其替换为第三个给定值。

【讨论】:

  • 哇!凉爽的!谢谢!
  • 有没有办法在不更新临时表的情况下做到这一点?当我把它直接变成SELECT switch_xml.modify ... from @xml 时,我得到“不正确地使用 XML 数据类型方法 'modify'。在这种情况下需要一个非突变方法。”
  • 刚刚发现那部分 - .modify 只能直接工作,而不是通过 select (link to SO about this),并且由于我的数据提取可以是 3gb,因此可以肯定将其放入表变量/临时表中会给我一个主要的性能打击。嗯。我得看看费用是多少。
  • @mbourgon 不确定您的问题...您也可以在 SET 中对 xml 类型变量使用 modify
  • @mbourgon 在这种情况下,它可能会加快速度,如果您 1) 将 XML 复制到目标,然后使用 UPDATE.modify() 以及 WHERE 子句测试 @ 987654340@。尤其是如果很多行不需要更新,这可能会有很大帮助...
猜你喜欢
  • 1970-01-01
  • 2012-01-22
  • 1970-01-01
  • 1970-01-01
  • 2020-10-03
  • 2023-03-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多