在 T-SQL XML value() 中使用属性变量的问题

Issue using attribute variable in T-SQL XML value()

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 函数是不允许的。

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

更简单一点的可能是:

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)

简而言之:

  • 深入 <B>(或者使用 深度搜索// 如果你能确定,不会有 <C> 在任何其他地方)
  • 查找任何具有给定名称的元素
  • 选择具有给定名称的属性(每个元素每个定义的属性都是单例的)
  • 当前节点上使用value()到return内容。

什么可能会干扰此:<C> 多次出现在 <B>

以下

更新 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.

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

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