【问题标题】:Retrieve all XML elements with the same prefix in SQL Server检索 SQL Server 中具有相同前缀的所有 XML 元素
【发布时间】:2017-02-06 22:50:46
【问题描述】:

我有一个类似于以下格式的 XML 文件:

<XML>
   <Field1>100</Field1>
   <Field2>200</Field2>
   <Field3>300</Field3>
   <Test>400</Test>
</XML>

我需要编写一个查询来获取所有以Field 开头的元素值。所以给定上面的 XML 结果应该是

FieldVal
--------
100     
200     
300

我尝试了以下方法,但它不起作用:

Select 
    xc.value('text()', 'int')
From 
    @XMLData.nodes('/XML/[starts-with(name(), ''Field'')]') As xt(xc)

注意:我很清楚,如果我重新格式化我的 XML,这项任务很容易完成,但不幸的是我无法控制 XML 的格式。

【问题讨论】:

  • 如果要获取以Field 开头的值,您的预期输出如何包含400
  • @techspider 啊是的好点。那是我的一个错误。我更新了我的预期输出。

标签: sql sql-server xml tsql xquery-sql


【解决方案1】:

一种方法是

declare @XMLData xml ='<XML>
   <Field1>100</Field1>
   <Field2>200</Field2>
   <Field3>300</Field3>
   <Test>400</Test>
</XML>'

Select 
    xc.value('.', 'int')
From @XMLData.nodes('/XML/*') As xt(xc)
WHERE xc.value('local-name(.)', 'varchar(50)') LIKE 'Field%'

【讨论】:

  • 这正是我所需要的。我不知道您可以在 where 子句中使用该值。我还没有看到一个例子。非常感谢!
  • @pagspi 在如此小的 XML 中,这绝对没问题,但在更大的结构(或其中很多)中,我更喜欢您不必阅读 所有数据 的方法b> 只是为了过滤它之后。在我看来,您尝试将过滤器 放在 XQuery 中 更好...
  • @Shnugo:SQL 中的 XML 处理通常性能很差(与关系处理相比)。在这种情况下,我什至不敢打赌在 XML 内部或外部进行过滤是否更好。我会说所有解决方案都是可行的,直到测量显示不然。 (那和 SQL 中的 XQuery 在功能上非常有限,所以一些过滤甚至是不可能的。)
  • @JeroenMostert,关于 性能 的另一个想法... 使用具有数百个节点的 XML 并且这来自具有许多行的表,我敢打赌 @987654322 @ 和local-name(.) 上的迟到的WHERE-filter 会更糟。仅仅是因为糟糕的性能 XML 引擎将不得不读取许多不需要的节点,最终将其丢弃。像这样的一个小例子并没有什么明显的区别......但是早期减少是一个非常普遍的原则......
  • @Shnugo:XML 引擎无论如何都会读取所有这些节点以过滤它们,它无法优化过滤器功能。那么问题是向关系引擎提供不必要的值是否比应用过滤器函数开销更大,而且我没有数字。我预计早期过滤也会更快,但就像我说的那样,我不会赌它。你说得对,一般来说,你希望过滤尽可能接近源。
【解决方案2】:

使用特殊字符前缀名称并检查包含代替。

declare @x xml ='<XML>
   <Field1>100</Field1>
   <Field2>200</Field2>
   <Field3>300</Field3>
   <Test>400</Test>
</XML>';

select t.n.value('.','varchar(100)')
from @x.nodes ('XML/*[contains(concat("$",local-name()),"$Field")]') t(n);

【讨论】:

    【解决方案3】:

    我想这就是你要找的东西:

    DECLARE @xml XML=
    '<XML>
       <Field1>100</Field1>
       <Field2>200</Field2>
       <Field3>300</Field3>
       <Test>400</Test>
    </XML>';
    
    SELECT Fld.value('.','int') AS FieldOnly
    FROM @xml.nodes('/XML/*[substring(local-name(.),1,5)="Field"]') AS A(Fld)
    

    只是因为cmets里的讨论:

    DECLARE @fldName VARCHAR(100)='Field';
    SELECT Fld.value('.','int') AS FieldOnly
    FROM @xml.nodes('/XML/*[substring(local-name(.),1,string-length(sql:variable("@fldName")))=sql:variable("@fldName")]') AS A(Fld)
    

    将第一行更改为“Test”(区分大小写!),你会得到只有 400 的一行...

    【讨论】:

    • 看起来很简洁。 +1
    • 5 替换为string-length("Field") 会使代码更加明显。尽管如此,它还是痛苦地证明了支持的 XQuery 方言的局限性。
    • 感谢@Shnugo,这个解决方案也很有效,它可以在 xquery 中处理所有问题,因此我不必加载整个 XML 和过滤器。
    • @JeroenMostert,好吧,如果这个“字段”是硬编码的,我看不到优势。你会发现两倍的文字......如果你需要动态地使用它,可以使用一个变量并使用let $var:= ...string-length()sql:variable("@varName") 来完全通用:-)
    • 优点是您不必解释(或手动计算)5。魔法常数总是不确定的。在这种情况下,如果您决定要匹配 Fields,那么忘记更新 5 比忘记更新 Field 的两个实例更容易。
    猜你喜欢
    • 2022-12-13
    • 1970-01-01
    • 2013-04-15
    • 1970-01-01
    • 1970-01-01
    • 2019-09-14
    • 2014-10-06
    • 1970-01-01
    • 2018-08-13
    相关资源
    最近更新 更多