【问题标题】:SQL Server Xml query with multiple namespaces具有多个命名空间的 SQL Server Xml 查询
【发布时间】:2025-11-30 10:55:01
【问题描述】:

我在 SQL Server 中有一个包含 Xml 列的表,但我无法查询它。我对 XPath 的了解还不够,无法确定我的查询是否错误,或者是因为名称空间似乎有冲突。这是一个示例 xml:

<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"
            xmlns:a="http://www.w3.org/2005/08/addressing" 
            xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
    <s:Header>
        <!-- snip -->
    </s:Header>
    <s:Body>
        <FetchRequest xmlns="http://www.foobar.org/my/schema">
            <Contract xmlns:a="http://www.foobar.org/2014/04/datacontracts"
                      xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
                <a:RequestedBy>John Doe</a:RequestedBy>
                <a:TransactionId>ABC20140402000201</a:TransactionId>
            </Contract>
        </FetchRequest>
    </s:Body>
</s:Envelope>

我想从 xml 中检索 TransactionId。我试过的查询是这样的:

SELECT TOP 100
MessageXml,
MessageXml.value('
    declare namespace s="http://www.w3.org/2003/05/soap-envelope";
    declare namespace a="http://www.w3.org/2005/08/addressing";
    (/s:Envelope/s:Body/FetchRequest/Contract/a:TransactionId)[1]', 'varchar(max)')
FROM dbo.Message

我的 MessageXml.value 将返回 NULL。如果我在 s:Body 之后删除所有内容,我似乎会得到一堆串联的文本,但是一旦我添加 FetchRequest,我的结果中就会返回 NULL。

我确实注意到 Contract 元素定义了 a 的命名空间,而 Envelope 也定义了 a 的命名空间,但我不确定这是否有问题。

在给定上述 xml 示例的情况下,如何使用 XPath 查询检索 TransactionId?

【问题讨论】:

    标签: sql-server xml xpath


    【解决方案1】:

    我知道答案是可以接受的,但实际上有更简单的方法,如果您唯一需要做的就是选择节点值。只需使用* 作为命名空间名称:

    SELECT MessageXml
         ,  MessageXml.value('(/*:Envelope/*:Body/*:FetchRequest/*:Contract/*:TransactionId)[1]'
                           , 'varchar(max)')
    FROM   dbo.Message
    

    【讨论】:

    • 这是我唯一可以接受的答案。我在一个 3rd-Party 数据库中工作,他们在同一列中使用各种命名空间,但它们大多遵循相同的模式(至少是我关心提取的模式路径)。这种方法还意味着更简单/更少的脚本,并避免在命名空间更改(或版本化以添加更多模式)时随着时间的推移而中断。谢谢!
    • @MikeTeeVee 这正是我找到它时的情况。很高兴它有帮助!
    • 这正是我所需要的,谢谢
    【解决方案2】:

    你有两个问题

    • 您没有遵守 &lt;FetchRequest&gt; 节点上的隐式默认 XML 命名空间
    • 带有 a: 前缀的 XML 命名空间首先在 &lt;s:Envelope&gt; 节点上定义,然后在 &lt;Contract&gt; 节点上重新声明(在我看来真的很糟糕的做法),您需要使用 second 声明&lt;Contract&gt; 节点下的任何内容。

    所以你需要这样的东西(我更喜欢预先定义 XML 命名空间,在 WITH XMLNAMESPACES() 语句中):

    ;WITH XMLNAMESPACES('http://www.w3.org/2003/05/soap-envelope' AS s,
                        'http://www.foobar.org/2014/04/datacontracts' AS a,
                        'http://www.foobar.org/my/schema' AS fb)
    SELECT 
        MessageXml,
        MessageXml.value('(/s:Envelope/s:Body/fb:FetchRequest/fb:Contract/a:TransactionId)[1]', 'varchar(max)')
    FROM 
        dbo.Message
    

    这将为您的第二列输出整个查询和值ABC20140402000201

    【讨论】:

    • 我只是评论说我没有创建 XML 模式,我只是想查询它。
    • 在修复我的谓词并遵循这个之后,我能够得到结果。感谢您的帮助!
    • 感谢分享,我一直在为多个域名而苦苦挣扎。这拯救了我的一天。
    【解决方案3】:

    FetchRequestContract 位于命名空间 http://www.foobar.org/my/schema 中,别名 a 在文档中重新定义。你需要:

    SELECT TOP 100
    MessageXml,
    MessageXml.value('declare namespace s="http://www.w3.org/2003/05/soap-envelope";
        declare namespace a="http://www.w3.org/2005/08/addressing";
        declare namespace f="http://www.foobar.org/my/schema";
        declare namespace x="http://www.foobar.org/2014/04/datacontracts";
        (/s:Envelope/s:Body/f:FetchRequest/f:Contract/x:TransactionId)[1]', 'varchar(max)')
    FROM dbo.Message;
    

    Fiddle here:

    【讨论】: