XML中的动态节点的SQL查询

时间:2017-03-09 13:49:56

标签: sql sql-server xml for-xml-path select-for-xml

我们的表就像

StudentNo  Name  Subject  Mark   Grade
1          John   English  41     A
1          John   Hindi    42     B 

我们希望此表格中的XML格式如下所示。

<Student>
    <Name>John</Name>
  <Subject>
    <English>
          <Mark>41</Mark>
          <Grade>A</Grade>
    </English>
    <Hindi>
          <Mark>42</Mark>
          <Grade>B</Grade>
    </Hindi>
 </Subject>
<Student>

这里应该动态生成主题名称节点。

提前感谢您的答案。

1 个答案:

答案 0 :(得分:2)

这与SQL Data as XML Element非常相似 - 以至于我认为这可能是重复的 - 但我想为你的背景解释一下为什么这不是最好的主意。在我对这个问题的回答中,我展示了一种非常黑客的方法,你可以做到这一点,但这不是最好的主意。

您的XML几乎不可能为其创建架构。该XML的任何使用者永远无法确定哪些值可能显示为元素。您应该使用某种属性,而不是尝试创建动态元素。你甚至可以使用xsi:type在你的XML中创建一个抽象类型(虽然在我的例子中我只是使用一个普通的旧属性 - 你可以选择对你的消费者最有意义的任何属性) 。该XML的查询将是:

declare @subjects TABLE(studentno int, name varchar(10), subjecT varchar(10), mark int, grade char(1))

INSERT @subjects
VALUES
(1, 'John','English',  41,'A'),
(1, 'John','Hindi',    42,'B')

select 
    s.Name

    ,(SELECT
        s2.Subject as '@type'
        ,s2.Mark
        ,s2.Grade
    FROM @subjects s2 
    WHERE s2.studentno = s.studentno
    FOR XML PATH('Subject'), ROOT('Subjects'), TYPE)
from @subjects s
GROUP BY s.name, s.studentno
FOR XML PATH('Student')

产生

<Student>
  <Name>John</Name>
  <Subjects>
    <Subject type="English">
      <Mark>41</Mark>
      <Grade>A</Grade>
    </Subject>
    <Subject type="Hindi">
      <Mark>42</Mark>
      <Grade>B</Grade>
    </Subject>
  </Subjects>
</Student>

这种XML可以被消费者理解,例如,他们可以在不知道主题可能是什么的情况下迭代主题(并且无需假设Subjects的每个直接孩子。实际上是一个主题,而不是在新版本的模式中添加的其他类型的节点。

如果真的需要输出,我更倾向于使用XSLT将上面的输出转换为您的格式,例如:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="xml" encoding="UTF-8" indent="yes" />

    <xsl:template match="node()|@*">
     <xsl:copy>
         <xsl:apply-templates />
     </xsl:copy>
    </xsl:template>

    <xsl:template match="Subject">
        <xsl:element name="{@type}">
            <xsl:apply-templates />
        </xsl:element>
    </xsl:template>

    <xsl:template match="Subjects">
        <xsl:element name="Subject">
            <xsl:apply-templates />
        </xsl:element>
    </xsl:template>
</xsl:transform>

让你

<?xml version="1.0" encoding="UTF-8"?>
<Student>
  <Name>John</Name>
  <Subject>
      <English>
         <Mark>41</Mark>
         <Grade>A</Grade>
      </English>
      <Hindi>
         <Mark>42</Mark>
         <Grade>B</Grade>
      </Hindi>
  </Subject>
</Student>

请注意,您无法完全使用SQL Server执行此操作 - 您不得不求助于构建XML字符串并将其转换为XML,就像我的其他答案一样。