如何从SQL中的XML获取指定的节点

时间:2019-03-27 16:18:44

标签: sql sql-server tsql

我正在尝试从XML数据中获取特定的节点,但我终生无法使其正常工作。我在变量和表中有以下XML(两者中的数据相同):

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <soap:Body>
    <HCNSearchResponse xmlns="http://testurl.com/">
      <HCNSearchResult>
        <HCNLookupResult>
          <MsgID>test1</MsgID>
          <Results>
            <DemographicDetails>
              <Title>Ms</Title>
              <Forename1>F1 test</Forename1>
              <Forename2 />
              <Forename3>F3 test</Forename3>
              <Sex>F</Sex>
              <DateOfBirth>01/01/2000</DateOfBirth>
              <Surname>test1</Surname>
            </DemographicDetails>
            <DemographicDetails>
              <Title>Mr</Title>
              <Forename1>F1 test</Forename1>
              <Forename2 />
              <Forename3></Forename3>
              <Sex>M</Sex>
              <DateOfBirth>01/01/2000</DateOfBirth>
              <Surname>test2</Surname>
            </DemographicDetails>
          </Results>
        </HCNLookupResult>
      </HCNSearchResult>
    </HCNSearchResponse>
  </soap:Body>
</soap:Envelope>

我试图仅从中提取“结果”节点,但我无法使其正常工作,我已经尝试了所有这些方法:

    SELECT @XMLResult.query('declare namespace 
    ns="http://testurl.com/";
    /ns:HCNSearchResponse/ns:HCNSearchResult/ns:HCNLookupResult/ns:Results')

    SELECT @XMLResult.query('/HCNSearchResponse/HCNSearchResult/HCNLookupResult/Results')

    SELECT T.N.query('.')
    FROM @XMLResult.nodes('/root/Body/Envelope/HCNSearchResponse/HCNSearchResult/HCNLookupResult/Results') as T(N)

    SELECT x.*, y.c.query('.')
    FROM #xml x
    CROSS APPLY x.resultsXML.nodes('/HCNSearchResponse/HCNSearchResult/HCNLookupResult/Results') y(c)

这些都没有完成!这些语句的select语句仅返回空结果。我有一个名为#XML的表,其中有一行,其中XML存储在名为“ resultsXML”的列中,而相同的数据存储在名为@XMLResult的变量中。有人可以帮助我吗?

如果有什么不同,这就是我在xml表中填充该列的方式:

INSERT #XML ( resultsXML )
EXEC sp_OAGetProperty @Obj, 'responseXML.XML'

2 个答案:

答案 0 :(得分:3)

您与第一个查询非常接近,但是必须声明所有使用的名称空间。您忘了声明肥皂的名称空间。请参阅下面的工作查询。

makemigrations

答案 1 :(得分:2)

有一个正确的答案,简单奶酪和一个完整的答案:

您的XML:

DECLARE @XMLResult XML=
N'<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <soap:Body>
    <HCNSearchResponse xmlns="http://testurl.com/">
      <HCNSearchResult>
        <HCNLookupResult>
          <MsgID>test1</MsgID>
          <Results>
            <DemographicDetails>
              <Title>Ms</Title>
              <Forename1>F1 test</Forename1>
              <Forename2 />
              <Forename3>F3 test</Forename3>
              <Sex>F</Sex>
              <DateOfBirth>01/01/2000</DateOfBirth>
              <Surname>test1</Surname>
            </DemographicDetails>
            <DemographicDetails>
              <Title>Mr</Title>
              <Forename1>F1 test</Forename1>
              <Forename2 />
              <Forename3></Forename3>
              <Sex>M</Sex>
              <DateOfBirth>01/01/2000</DateOfBirth>
              <Surname>test2</Surname>
            </DemographicDetails>
          </Results>
        </HCNLookupResult>
      </HCNSearchResult>
    </HCNSearchResponse>
  </soap:Body>
</soap:Envelope>';

-正确的答案已经由@bdebaere提供。如果要使用它,请在此处设置接受度(但是您当然可以赞成;-))。
-但是您可以使用一个名称空间声明来明确说明所有内容:
-优点:如果使用了几次对XML方法的调用,则必须一遍又一遍地重复声明...

WITH XMLNAMESPACES(DEFAULT 'http://testurl.com/'
                          ,'http://schemas.xmlsoap.org/soap/envelope/' AS [soap])
SELECT @XMLResult.query('/soap:Envelope/soap:Body/HCNSearchResponse/HCNSearchResult/HCNLookupResult/Results');

-轻松奶酪方法使用深度搜索和名称空间通配符
-一般建议是:尽可能具体,但有时懒惰者会赢...

SELECT @XMLResult.query('//*:Results') 

-完全确定的答案是这样:

WITH XMLNAMESPACES(DEFAULT 'http://testurl.com/'
                          ,'http://schemas.xmlsoap.org/soap/envelope/' AS [soap])
SELECT dd.value('(Title/text())[1]','nvarchar(max)') AS Title
      ,dd.value('(Forename1/text())[1]','nvarchar(max)') AS Forename1
      ,dd.value('(Forename2/text())[1]','nvarchar(max)') AS Forename2
      ,dd.value('(Forename3/text())[1]','nvarchar(max)') AS Forename3
      ,dd.value('(Sex/text())[1]','nvarchar(1)') AS Sex
      ,dd.value('(DateOfBirth/text())[1]','nvarchar(max)') AS DateOfBirth --Hint: don't use 'datetime' here. Rather pull this data as string and use CONVERT with the appropriate style hint
      ,dd.value('(Surname/text())[1]','nvarchar(max)') AS Surname
FROM @XMLResult.nodes('/soap:Envelope/soap:Body/HCNSearchResponse/HCNSearchResult/HCNLookupResult/Results/DemographicDetails') A(dd);

结果

+-------+-----------+-----------+-----------+-----+-------------+---------+
| Title | Forename1 | Forename2 | Forename3 | Sex | DateOfBirth | Surname |
+-------+-----------+-----------+-----------+-----+-------------+---------+
| Ms    | F1 test   | NULL      | F3 test   | F   | 01/01/2000  | test1   |
+-------+-----------+-----------+-----------+-----+-------------+---------+
| Mr    | F1 test   | NULL      | NULL      | M   | 01/01/2000  | test2   |
+-------+-----------+-----------+-----------+-----+-------------+---------+