【问题标题】:'local-name()' requires a singleton (or empty sequence) T-SQL Xquery'local-name()' 需要一个单例(或空序列)T-SQL Xquery
【发布时间】:2020-05-11 06:58:36
【问题描述】:

这是这两个问题的变体/组合,但我想我已经按照他们的描述做了:

有没有办法在 SQL Server 2016 T-SQL 查询中获取节点的名称?

我想这样做:

select 
    trcid,
    trcXML.value(
        'local-name(//*[local-name()="after"][1]/*[1])',  
        'varchar(32)'
    ) as XmlTableName,
    trcXml, 
    trcGUID, 
    trcCorrID 
from trace 
order by trcId desc

我已在层次结构中的每个项目之后明确包含[1]

错误:XQuery [trace.trcXML.value()]: 'local-name()' 需要一个单例(或空序列),找到'element(*,xdt:untyped) *' 类型的操作数

样本数据:

<s1:DB2Request xmlns:s1="XYZDB2">
  <s1:sync>
    <s1:after identityInsert="false">
      <s1:PG204AT5>
        <s1:ISA06>1975111</s1:ISA06>
           etc... 
      </s1:PG204AT5>
    </s1:after>
  </s1:sync>
</s1:DB2Request>

我想要位于“after”和 ISA06 元素之间的表名(这是一个 BizTalk“update-gram”),在本例中为:PG204AT5。

在 xpathtester.com 中测试:local-name(//*[local-name()="after"][1]/* [1]) - 出于某种原因,当我保存它时,XPATH 发生了变化:http://www.xpathtester.com/xpath/2bd602d8fc7aee0484de14bf93f71ef2

【问题讨论】:

  • Node 是什么意思?是服务器名称吗? SELECT @@servername AS '服务器名' ,@@servicename AS '实例名' ,DB_NAME() AS '数据库名' ,HOST_NAME() AS '主机名'

标签: sql sql-server xml tsql


【解决方案1】:

另外一组括号会根据需要为您提供一个单例:

DECLARE @t TABLE(s XML);
INSERT INTO @t(s)VALUES(N'<s1:DB2Request xmlns:s1="XYZDB2">
  <s1:sync>
    <s1:after identityInsert="false">
      <s1:PG204AT5>
        <s1:ISA06>1975111</s1:ISA06>
           <somethingsomething/>
      </s1:PG204AT5>
    </s1:after>
  </s1:sync>
</s1:DB2Request>');

select 
    s.value(
        'local-name((//*[local-name()="after"][1]/*)[1])',  
        'varchar(32)'
    ) as XmlTableName
from @t;

结果:

XmlTableName
PG204AT5

【讨论】:

    【解决方案2】:

    请记住,与大多数 XQuery 实现不同,SQL Server 引擎执行“悲观静态类型”——它假定如果事情可能出错,它们就会出错。

    表达式 //*[local-name()="after"][1]/* [1] 能够返回多个元素 - 例如,它会使用此输入来返回

    <x>
      <y>
        <after>
           <z/>
        </after>
      </y>
      <y>
        <after>
           <z/>
        </after>
      </y>
    </x>
    

    所以 XQuery 编译器推断出 element()* 的静态类型,不满足要求。

    简单地做(//*[local-name()="after"]/*)[1]可能就足够了。

    就个人而言,我一直认为悲观静态类型是一个非常糟糕的设计选择,但包括微软在内的一些早期 XQuery 实现者非常热衷于它。

    【讨论】:

    • 我不清楚为什么将第一个 [1] 放在不带括号的情况下不能作为下标。解析器是在用它做其他事情还是完全忽略它?
    • //X[1] 解析为/descendant-or-self::node()/(child::X[1]),它返回每个 X 是其父级的第一个子级。相比之下,`(//X)[1] 查找文档中的所有 X 元素,然后选择第一个。
    【解决方案3】:

    或者更简单的解决方案,明确询问&lt;s1:after&gt; 元素的第一个子节点名称。

    SQL

    -- DDL and sample data population, start
    DECLARE @tbl TABLE(xmldata XML);
    INSERT INTO @tbl(xmldata)
    VALUES(N'<s1:DB2Request xmlns:s1="XYZDB2">
      <s1:sync>
        <s1:after identityInsert="false">
          <s1:PG204AT5>
            <s1:ISA06>1975111</s1:ISA06>
               <somethingsomething/>
          </s1:PG204AT5>
        </s1:after>
      </s1:sync>
    </s1:DB2Request>');
    -- DDL and sample data population, end
    
    ;WITH XMLNAMESPACES (DEFAULT 'XYZDB2')
    SELECT xmldata.value('local-name((/DB2Request/sync/after/child::node())[1])','VARCHAR(50)') as XmlTableName
    FROM @tbl;
    

    输出

    +--------------+
    | XmlTableName |
    +--------------+
    | PG204AT5     |
    +--------------+
    

    【讨论】:

    • 谢谢,我选择了 TT. 的方法,所以我不必指定命名空间。
    • 命名空间不是强制性的。您可以使用命名空间通配符。检查一下:SELECT xmldata.value('local-name((//*:after/child::node())[1])','VARCHAR(50)') as XmlTableName FROM @tbl;
    • 我认为重点是我需要在最后 [1] 前面的所有内容周围加上括号。
    • 不需要调用两次local-name()函数。
    • SQL Server 支持 XQuery 1.0 和 XPath 2.0 的子集。这就是名称空间通配符功能到位的原因。
    猜你喜欢
    • 2021-06-01
    • 2016-06-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多