【问题标题】:Issue using attribute variable in T-SQL XML value()在 T-SQL XML value() 中使用属性变量的问题
【发布时间】:2020-10-06 19:29:18
【问题描述】:
DECLARE @NodePath VARCHAR(20) = 'C',
        @NodeVariable VARCHAR(20) = '@name',
        @result XML;
SET @result = '
<A>
    <B>
        <C name="Name01"/>
    </B>
    <B>
        <C name = "Name02"/>
    </B>
</A>
'
SELECT T.c.value('sql:variable("@NodeVariable")', 'VARCHAR(20)')
FROM @result.nodes('//*[local-name()=sql:variable("@NodePath")]') T(c)

我想获取XML的属性,比如:

|Name|
|Name01|
|Name02|

但结果是:

|Name|
|@name|
|@name|

如何解决这个问题?由于我想创建一个将路径和属性名称作为参数的函数,因此不允许使用 OPENXML 函数。

【问题讨论】:

    标签: sql-server xml tsql xml-parsing


    【解决方案1】:

    可能会更简单一点:

    SELECT x.n.value('.', 'nvarchar(20)') as 'Name'
    FROM @result.nodes('/A
                        /B
                        /*[local-name() =sql:variable("@NodePath")]
                        /@*[local-name()=sql:variable("@NodeVariable")]') x(n)
    

    简而言之:

    • 深入&lt;B&gt; 下方(或使用深度搜索//,如果你可以确定,在任何其他地方都不会有&lt;C&gt;
    • 查找具有给定名称的任何元素
    • 选择具有给定名称的属性(每个定义的每个元素的属性都是单例的)
    • 当前节点上使用value()返回内容。

    可能会造成干扰:&lt;C&gt; 多次出现在 &lt;B&gt; 下方

    更新 XPath 和 local-name() 的一些补充

    试试这个:

    declare @result xml =
    N'<A>
        <B>
            <C name="Name01"/>
        </B>
        <TheSecondInA />
        <B>
            <C name = "Name02"/>
        </B>
        <OneMore someAttr="x" oneMoreAttr="y" theLastAttr="z" >SomeText</OneMore>
    </A>';
    
    SELECT @result.value('local-name((//TheSecondInA)[1])','varchar(100)')
          ,@result.value('local-name((/A/*[2])[1])','varchar(100)')
          ,@result.value('local-name(/A[1]/*[2])','varchar(100)')
          ,@result.value('local-name((//*[@someAttr]/@*[2])[1])','varchar(100)')
          ,@result.value('local-name((/A/OneMore/@*[3])[1])','varchar(100)')
          ,@result.value('local-name((/A/OneMore/@*[last()])[1])','varchar(100)')
    
          ,@result.value('local-name((/A/OneMore/text())[1])','varchar(100)')
          ,@result.value('local-name((/DoesNotExist)[1])','varchar(100)')
    

    如您所见,函数local-name() 必须获得一个单例XPath

    • 深度搜索会深入到命名节点的第一次出现
    • &lt;A&gt; 下面的第二个元素返回相同的值
    • 如果路径本身保证返回单例,我们不需要这个 (SomeXpath)[1]
    • 在这里,我们深入到第一个元素,其中有一个名为 someAttr 的属性,并根据其位置选择第二个属性。
    • 类似地,我们可以选择给定路径上的第三个属性
    • 要获取最后一个属性(或元素),我们可以使用last()
    • 如果当前节点是text() 节点,或者该元素不存在,我们会返回一个空字符串。

    提示:使用类似的 XPath 表达式,您可以使用 .value() 检索本地内容,.exist() 测试是否存在(或不存在)并修改给定位置...

    【讨论】:

      【解决方案2】:

      value() 函数中,您需要使用@*[local-name()=...] 语法,例如:

      declare @NodePath nvarchar(20) = 'C';
      declare @NodeVariable nvarchar(20) = 'name';
      declare @result xml =
      N'<A>
          <B>
              <C name="Name01"/>
          </B>
          <B>
              <C name = "Name02"/>
          </B>
      </A>'
      select x.n.value('(@*[local-name()=sql:variable("@NodeVariable")])[1]', 'nvarchar(20)') as 'Name'
      from @result.nodes('//*[local-name()=sql:variable("@NodePath")]') x(n)
      

      产量:

      Name
      ------
      Name01
      Name02
      

      【讨论】:

      • 你能解释一下它是如何工作的吗?我以为local-name() 返回了元素名称?但是您使用它来返回属性名称 - 我真的很想知道如何:) 谢谢。还有为什么@*[中的“*”是必需的。
      • local-name() 函数在节点集上运行。当使用@*[] 时,节点集的上下文是属性名称而不是元素名称。
      • 我希望 W3C XPath 文档对此更加清晰。有一天我在阅读XPathNavigator.LocalName Property 时偶然发现,看到它谈论元素、属性、命名空间和处理指令的 XPathNodeTypes,然后想……嘿,如果?
      • @DaleK,我在local-name() 的答案中添加了一些内容,您可能会感兴趣...
      • @DaleK @*[] 是一个完整的通配符,与元素一样,因此将匹配所有命名空间的所有属性。您还可以使用@foo:*[] 仅匹配foo 命名空间前缀中的元素,或使用@*:bar[] 仅匹配名为bar 的属性(无论它们的命名空间前缀如何)。
      猜你喜欢
      • 2011-07-17
      • 2011-07-17
      • 2021-10-25
      • 1970-01-01
      • 1970-01-01
      • 2011-06-16
      • 2016-10-27
      • 1970-01-01
      • 2016-06-25
      相关资源
      最近更新 更多