为SQL Server中没有子节点的每个XML节点创建一列

时间:2018-08-15 15:23:39

标签: sql sql-server xml ssms

我正在尝试在SSMS中查询xml文件,以创建一个表,该表由第一行(所有xml节点的列表)和下一行(包含其所有值)组成。 xml数据将具有一个重复节点,该节点将为每个具有该名称的重复节点创建一个新行。这是xml数据的示例:

<Tests>
<TestAnswers>
    <Question1>
        <Name>Example</Name>
        <Age>23</Age>
        <Question1B>
            <Title>Singer</Title>
        </Questions1B>
    </Question1>
    <Question2>
        <Car></Car>
        <Model>Model 1</Model>
        <Question2B>
            <Year>1986</Year>
            <Manufactured></Manufactured>
        </Questions2B>
    </Question2>
</TestAnswers>
<TestAnswers>
    <Question1>
        <Name>Santa</Name>
        <Age></Age>
        <Question1B>
            <Title>Writer</Title>
        </Questions1B>
    </Question1>
    <Question2>
        <Car>This car</Car>
        <Model>Model2</Model>
        <Question2B>
            <Year></Year>
            <Manufactured></Manufactured>
        </Questions2B>
    </Question2>
</TestAnswers>
</Tests>

我要在SQL Server中创建的表应类似于:

   |    Name     | Age |    Title    |    Car    |    Model    |   Year   |    Manufactured    |
-------------------------------------------------------------------------------------------------
 1 | Example     | 23  | Singer      |           | Model 1     | 1986     |                    |
 2 | Santa       |     | Writer      | This Car  | Model2      |          |                    |

随着xml文件随着更多信息而变大,将创建更多行。我不想为包含子节点的xml节点创建列。

如何编写查询来执行此任务?

更新

新的XML用例,其中有两列包含重复的节点:

<Tests>
<TestAnswers>
    <Question1>
        <Name>Example</Name>
        <Age>23</Age>
        <Other>Other Text</Other>
        <Question1B>
            <Title>Singer</Title>
            <Other></Other>
        </Question1B>
    </Question1>
    <Question2>
        <Car></Car>
        <Model>Model 1</Model>
        <Question2B>
            <Year>1986</Year>
            <Manufactured></Manufactured>
        </Question2B>
    </Question2>
</TestAnswers>
<TestAnswers>
    <Question1>
        <Name>Santa</Name>
        <Age></Age>
        <Other></Other>
        <Question1B>
            <Title>Writer</Title>
            <Other>Text</Other>
        </Question1B>
    </Question1>
    <Question2>
        <Car>This car</Car>
        <Model>Model2</Model>
        <Question2B>
            <Year></Year>
            <Manufactured></Manufactured>
        </Question2B>
    </Question2>
</TestAnswers>
</Tests>

1 个答案:

答案 0 :(得分:1)

此XML是否在您的控制之下?

除了它是无效的事实(子元素<Question2B>的结束标记是复数<Questions2B>)之外,还有严重的设计缺陷...

最明显的是名称编号,例如<Question1><Question2> ...

如果内容也可能有所不同,请提供更多详细信息。您正在编写更多行更多信息。您是说更多相同还是可能还有其他列甚至其他结构?

除了这一切以外,还有一些通用的方法来展示原理:

DECLARE @xml XML=
N'<Tests>
<TestAnswers>
    <Question1>
        <Name>Example</Name>
        <Age>23</Age>
        <Question1B>
            <Title>Singer</Title>
        </Question1B>
    </Question1>
    <Question2>
        <Car></Car>
        <Model>Model 1</Model>
        <Question2B>
            <Year>1986</Year>
            <Manufactured></Manufactured>
        </Question2B>
    </Question2>
</TestAnswers>
<TestAnswers>
    <Question1>
        <Name>Santa</Name>
        <Age></Age>
        <Question1B>
            <Title>Writer</Title>
        </Question1B>
    </Question1>
    <Question2>
        <Car>This car</Car>
        <Model>Model2</Model>
        <Question2B>
            <Year></Year>
            <Manufactured></Manufactured>
        </Question2B>
    </Question2>
</TestAnswers>
</Tests>';

-这将返回所有答案逐行

SELECT a.query('.')
FROM @xml.nodes('/Tests/TestAnswers') A(a);

-这将返回所有问题逐行

SELECT q.query('.')
FROM @xml.nodes('/Tests/TestAnswers') A(a)
OUTER APPLY a.nodes('*') B(q);

-这将返回带有自己的文本逐行

的所有节点
SELECT nd.query('.')
FROM @xml.nodes('/Tests/TestAnswers') A(a)
OUTER APPLY a.nodes('*') B(q)
OUTER APPLY q.nodes('//*[text()]') C(nd);

更新

尝试使用此方法获取列表,然后每PIVOT使用aInx来获取表格格式:

WITH Answers AS
(
    SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS aInx
          ,a.query('.') AS a
    FROM @xml.nodes('/Tests/TestAnswers') A(a)
)
,Questions AS
(
    SELECT aInx
          ,q.query('.') AS q
    FROM Answers
    OUTER APPLY a.nodes('*') B(q)
)
SELECT aInx
      ,nd.value('local-name(.)','nvarchar(max)') AS NodeName
      ,nd.value('text()[1]','nvarchar(max)') AS NodeValue
FROM Questions
OUTER APPLY q.nodes('//*[text()]') C(nd);