SQL 服务器 XML 列中的部分通配符搜索

Partial Wildcard Searches in SQL Server XML Columns

我在 MS SQL Server 2012 数据库中有一个 XML 列,我需要为其创建一个查询以将节点提取到 table 中。这是 XML 结构的要点:

<root>
  <ProjectInfo>
  <PrimaryContact>
    <Name>
    <Phone>
    <Email>
  </PrimaryContact>
  <SecondaryContact>
    <Name>
    <Phone>
    <Email>
  </SecondaryContact>
  <TechnicalContact>
    <Name>
    <Phone>
    <Email>
  </TechnicalContact>
  <BillingContact>
    <Name>
    <Phone>
    <Email>
  </BillingContact>
  <OtherStuff>
</root>

我正在尝试编写一个查询,从每个联系人节点获取姓名、Phone 和电子邮件。不幸的是,XPath /root/*Contact/ 不合法。我知道我可以编写一个组合了一堆 UNION 的查询来合并列,但我觉得可能有一种我目前不知道的更简单的方法。

有没有办法使用通配符或某种 OR-ing 机制来从每个 *Contact 节点检索姓名、Phone 和电子邮件?

注意:我不能使用 /root/*/Name,因为还有其他节点将 Name 作为下一个不用于联系人的内部节点,并且 Phone 和 Email 都是可选字段。

您可以看看这个工作示例:

DECLARE @xml XML=
'<root>
  <ProjectInfo>
    <Name value="this not"/>
  </ProjectInfo>
  <PrimaryContact>
    <Name value="x"/>
    <Phone value="y"/>
    <Email value="z"/>
  </PrimaryContact>
  <SecondaryContact>
    <Name value="a"/>
    <Phone value="b"/>
    <Email value="c"/>
  </SecondaryContact>
  <TechnicalContact>
    <Name value="e"/>
    <Phone value="f"/>
    <Email value="g"/>
  </TechnicalContact>
  <BillingContact>
    <Name value="m"/>
    <Phone value="n"/>
    <Email value="o"/>
  </BillingContact>
  <OtherStuff>
    <Name value="don''t include"/>
  </OtherStuff>
</root>';

SELECT Level1.value('local-name(.)','nvarchar(max)') AS Level1_Name
      ,Level2.value('local-name(.)','nvarchar(max)') AS Level2_Name
      ,Level2.value('@value','nvarchar(max)') AS Level2_Value
FROM @xml.nodes('/root/*[fn:contains(local-name(),"Contact")]') A(Level1)
CROSS APPLY Level1.nodes('*') AS B(Level2);

结果

+------------------+-------+---+
| PrimaryContact   | Name  | x |
+------------------+-------+---+
| PrimaryContact   | Phone | y |
+------------------+-------+---+
| PrimaryContact   | Email | z |
+------------------+-------+---+
| SecondaryContact | Name  | a |
+------------------+-------+---+
| SecondaryContact | Phone | b |
+------------------+-------+---+
| SecondaryContact | Email | c |
+------------------+-------+---+
| TechnicalContact | Name  | e |
+------------------+-------+---+
| TechnicalContact | Phone | f |
+------------------+-------+---+
| TechnicalContact | Email | g |
+------------------+-------+---+
| BillingContact   | Name  | m |
+------------------+-------+---+
| BillingContact   | Phone | n |
+------------------+-------+---+
| BillingContact   | Email | o |
+------------------+-------+---+

只需去掉 [fn:contains(local-name(),"Contact")],您就会看到 ProjectInfoOtherStuff 的名称-值。

如果您需要并排排列您的列,您可以使用 PIVOT

SELECT p.*
FROM
(
    SELECT Level1.value('local-name(.)','nvarchar(max)') AS Level1_Name
          ,Level2.value('local-name(.)','nvarchar(max)') AS Level2_Name
          ,Level2.value('@value','nvarchar(max)') AS Level2_Value
    FROM @xml.nodes('/root/*[fn:contains(local-name(),"Contact")]') A(Level1)
    CROSS APPLY Level1.nodes('*') AS B(Level2)
) AS tbl
PIVOT
(
    MIN(Level2_Value) FOR Level2_Name IN(Name,Phone,Email)
) AS p;

结果:

+------------------+------+-------+-------+
| Level1_Name      | Name | Phone | Email |
+------------------+------+-------+-------+
| BillingContact   | m    | n     | o     |
+------------------+------+-------+-------+
| PrimaryContact   | x    | y     | z     |
+------------------+------+-------+-------+
| SecondaryContact | a    | b     | c     |
+------------------+------+-------+-------+
| TechnicalContact | e    | f     | g     |
+------------------+------+-------+-------+